<style>
.informative-bg {
  margin: 1.5em 0 1em;
  padding: 1em;
  margin-top: 1em;
  background: #efe;
  border: green 1px dotted;
}
@media (prefers-color-scheme: dark) {
  .informative-bg {
    background: rgba(255, 255, 255, .05);
  }
}

div.informative-bg *:last-child {
  margin-bottom: 0;
}

div.informative-bg p:first-child {
  margin-top: 0;
}

div.informative-bg h2,
div.informative-bg h3,
div.informative-bg h4 {
  background: none;
}

/* crbug.com/1471465 */
dl.switch > dt {
  counter-increment: list-item 0;
}

.attributes::before, .methods::before,
.parameters::before, .exceptions::before,
.constructors::before, .members::before {
  font: bold 100% sans-serif;
  text-align: left;
  margin: 1.33em 0px;
  color: var(--heading-text, #005A9C);
}

.attributes::before { content: 'Attributes' }
.methods::before { content: 'Methods' }
.parameters::before { content: 'Parameters' }
.exceptions::before { content: 'Exceptions' }
.constructors::before { content: 'Constructors' }
.members::before { content: 'Dictionary members' }

dl.switch  > [data-md] > p {
  display: inline;
}

dl.switch > dt > ul > li {
  text-indent: 0;
}
</style>

<pre class='metadata'>
Title: Web Animations Module Level 2
Status: WD
Date: 2025-11-20
Prepare for TR: yes
Work Status: Exploring
Shortname: web-animations
ED: https://drafts.csswg.org/web-animations-2/
TR: https://www.w3.org/TR/web-animations-2/
Version history: https://github.com/w3c/csswg-drafts/commits/main/web-animations-2
Level: 2
!Delta Spec: yes

Group: csswg
!Participate: <a href="https://github.com/w3c/csswg-drafts/tree/main/web-animations-2">Fix the text through GitHub</a>
!Participate: Join the &lsquo;waapi&rsquo; channel on the <a href="https://damp-lake-50659.herokuapp.com/">Animation at Work</a> slack
!Participate: IRC: <a href="ircs://irc.w3.org:6667/webanimations">#webanimations</a> on W3C's IRC
Repository: w3c/csswg-drafts
!Issue Tracking: <a href="https://github.com/w3c/csswg-drafts/labels/web-animations-2">GitHub</a>

Editor: Brian Birtles 43194, Invited Expert, brian@birchill.co.jp
Editor: Robert Flack 98451, Google, flackr@google.com
Former Editor: Shane Stephens 47691, Google, shans@google.com
Former Editor: Douglas Stockwell, Google, dstockwell@google.com
Abstract: This specification defines a model for synchronization and
    timing of changes to the presentation of a Web page.
    This specification also defines an application programming interface
    for interacting with this model and it is expected that further
    specifications will define declarative means for exposing these
    features.
Ignored Vars: auto-rewind, current direction, index, initial progress,
    timeline, new timeline, t, going forwards, did trigger
WPT Path Prefix: web-animations
WPT Display: open
</pre>
<pre class="anchors">
urlPrefix: https://drafts.csswg.org/web-animations-1/; type: dfn; spec: web-animations-1
    text: active-after boundary time
    text: active interval
    text: active time
    text: animation
    text: animation direction
    text: animation effect
    text: Apply any pending playback rate
    text: associated effect end
    text: before-active boundary time
    text: cancel an animation
    text: current iteration index
    text: current ready promise
    text: current finished promise
    text: directed progress
    text: effective playback rate
    text: effect value
    text: end delay
    text: end time
    text: fill mode
    text: in effect
    text: iteration count
    text: iteration duration
    text: iteration progress
    text: iteration start
    text: keyframe effect
    text: monotonically increasing timeline
    text: not animatable
    text: overall progress
    text: pending pause task
    text: pending play task
    text: pending playback rate
    text: play an animation
    text: playback direction
    text: pause an animation
    text: reverse an animation
    text: ready
    text: set the timeline of an animation
    text: simple iteration progress
    text: target property
    text: time value
    text: timeline
    text: transformed progress
    text: update animations and send events
    text: update the timing properties of an animation effect
    text: unresolved
    text: update an animation's finished state
    text: default document timeline
urlPrefix: https://webidl.spec.whatwg.org/; type: dfn; spec: webidl
    text: resolve a promise; url: resolve
urlPrefix: http://www.ecma-international.org/ecma-262/6.0/#sec-; type: dfn; spec: ecma-262
    text: code realms
    text: execution contexts
urlPrefix: https://drafts.csswg.org/scroll-animations-1/; type: dfn; spec: scroll-animations-1
    text: event-loop
</pre>

<pre class=link-defaults>
spec:webidl; type:dfn; text:throw
</pre>

<h2 id="delta">Delta specification</h2>

<p>This is a delta specification, meaning that it currently contains
only the differences from Web Animations Level 1 [[!WEB-ANIMATIONS-1]].
Once the Level 1 specification is closer to complete, it will be merged
with the additions here into a complete level 2 specification.</p>

<h2 id="timing-model">Timing model</h2>

This section describes and defines the behavior of the Web Animations
timing model.

<h3 id="timing-model-overview">Timing model overview</h3>

<div class='informative-bg'>

<em>This section is non-normative</em>

<h4 id="hierarchical">Hierarchical</h4>

This level of the specification includes an updated timing hierarchy diagram.

<figure>
  <img src="images/time-hierarchy.svg" width="600"
            alt="A hierarchy of timing nodes">
  <figcaption>
    A hierarchy of timing nodes.
    Each node in the tree derives its time from its parent node.
  </figcaption>
</figure>

Along with the following updated description:

>   A consequence of this hierarchical arrangement is that complex
>   animation arrangements can be reversed, scheduled, accelerated and so
>   on as a whole unit since the manipulations applied to the parent,
>   cascade down to its <a>descendants</a>.
>   Furthermore, since time has a common source, it is easy to synchronize
>   animations.

</div>


<h3 id="timelines">Timelines</h3>

Add:

> The <dfn lt="timeline duration">duration</dfn> of a timeline gives the
> maximum value a timeline may generate for its current time. This value is
> used to calculate the [=intrinsic iteration duration=] for the target effect
> of an animation that is associated with the timeline when the effect's
> [=iteration duration=] is "auto". The value is computed such that the effect
> fills the available time. For a monotonic timeline, there is no upper bound
> on current time, and [=timeline duration=] is unresolved. For a non-monotonic
> (e.g. scroll) timeline, the duration has a fixed upper bound. In this
> case, the timeline is a <dfn lt="progress-based timeline">progress-based
> timeline</dfn>, and its [=timeline duration=] is 100%.

<h3 id="animation-frame-loop">Animation Frames</h3>

Update the note on the first step of the algorithm by adding the following bullet:

> *   Updating the [=animation trigger state|state=] of [=animation triggers=]
>     by running the [=updating animation trigger state=] procedure.

<h3 id="animations">Animations</h3>

Append:

> Animation effects associated with a [=progress-based timeline=] require their
> timing properties to be converted to proportions. The procedure for converting
> a <dfn lt="time-based animation to a proportional animation"></dfn> is as
> follows:
>
> 1.  If the [=iteration duration=] is auto, then perform the following steps.
>     *   Set [=start delay=] and [=end delay=] to 0, as it is not
>         possible to mix time and proportions.
>
>         Note: Future versions may allow these properties to be assigned
>         percentages, at which point the delays are only to be ignored if
>         their values are expressed as times and not as percentages.
>
>     Otherwise:
>          1. Let <var>total time</var> be equal to [=end time=]
>          1. Set [=start delay=] to be the result of evaluating
>             <code>|specified start delay| / |total time| *
>             |timeline duration|</code>.
>          1. Set [=iteration duration=] to be the result of evaluating
>             <code>|specified iteration duration| / |total time| *
>             |timeline duration|</code>.
>          1. Set [=end delay=] to be the result of evaluating
>             <code>|specified end delay| / |total time| *
>             |timeline duration|</code>.
>
> The procedure to <dfn>normalize specified timing</dfn> is as follows:
>
> If [=timeline duration=] is resolved:
>     *   Follow the procedure to convert a [=time-based animation to a
>         proportional animation=]
> Otherwise:
>     1.   Set [=start delay=] = |specified start delay|
>     1.   Set [=end delay=] = |specified end delay|
>     1.   If [=iteration duration=] is auto:
>              *   Set [=iteration duration=] = [=intrinsic iteration duration=]
>          Otherwise:
>              *   Set [=iteration duration=] = |specified iteration duration|

### Setting the timeline of an animation ### {#setting-the-timeline}

The procedure to <dfn export for=animation>set the timeline of an animation</dfn>,
<var>animation</var>, to <var>new timeline</var> which may be null, is as
follows:

1.  Let <var>old timeline</var> be the current <a>timeline</a> of
    <var>animation</var>, if any.
1.  If <var>new timeline</var> is the same object as <var>old timeline</var>,
    abort this procedure.
1.  Let |previous play state| be |animation|'s [=play state=].
1.  Let |previous current time| be the |animation|'s [=animation/current time=].
1.  Set |previous progress| based in the first condition that applies:
    <dl class=switch>
        :   If |previous current time| is unresolved:

        ::  Set |previous progress| to unresolved.

        :   If [=end time=] is zero:

        ::  Set |previous progress| to zero.

        :   Otherwise

        ::  Set |previous progress| = <code>|previous current time| /
            [=end time=]</code>
    </dl>

1.  Let |from finite timeline| be true if |old timeline| is not null and
    not [=monotonically increasing timeline|monotonically increasing=].
1.  Let |to finite timeline| be true if |timeline| is not null and not
    [=monotonically increasing timeline|monotonically increasing=].
1.  Let the <a>timeline</a> of <var>animation</var> be <var>new timeline</var>.
1.  Perform the steps corresponding to the <em>first</em> matching
    condition from the following, if any:

    <dl class="switch">

        :   If |to finite timeline|,

        ::  1.  [=Apply any pending playback rate=] on |animation|
            1.  set |auto align start time| to true.
            1.  Set [=animation/start time=] to unresolved.
            1.  Set [=animation/hold time=] to unresolved.
            1.  If |previous play state| is "finished" or "running"
                1. Schedule a [=pending play task=]

            1.  If |previous play state| is "paused" and
                |previous progress| is resolved:
                1.  Set [=hold time=] to |previous progress| * [=end time=].

            <p class="note">
                This step ensures that |previous progress| is preserved
                even in the case of a pause-pending animation with a
                resolved [=animation/start time=].
             </p>

        :   If |from finite timeline| and |previous progress| is resolved,

        ::  Run the procedure to <a>set the current time</a> to
            |previous progress| * [=end time=].

    </dl>

1.  If the [=animation/start time=] of <var>animation</var> is <a
    lt=unresolved>resolved</a>, make <var>animation</var>'s <a>hold time</a>
    <a>unresolved</a>.

    <p class="note">
    This step ensures that the [=play state/finished|finished play state=] of
    <var>animation</var> is not &ldquo;sticky&rdquo; but is re-evaluated
    based on its updated [=animation/current time=].
    <p>

1.  Run the procedure to <a>update an animation's finished state</a> for
    <var>animation</var> with the <var>did seek</var> flag set to false, and
    the <var>synchronously notify</var> flag set to false.

<ins cite="#amend-CE">
Issue: If <var>new timeline</var> is null, we should ensure that <a>custom
effects</a> get called with an <a>unresolved</a> <a>iteration progress</a>
(unless a subsequent change in the same script execution context makes this
redundant).
</ins>

<h4 id="setting-the-target-effect">Setting the target effect of an
  animation</h4>

After the step to reschedule a <a>pending play task</a> add the following step:

> 1.  If <var>new effect</var> is not <code>null</code> and
>     if <var>new effect</var> has a <a>parent group</a>,
>     <a lt="remove an animation effect">remove</a> <var>new effect</var> from
>     its <a>parent group</a>.

<ins cite="#amend-CE">
After the step to assign <var>new effect</var> as <var>animation</var>'s
<a>associated effect</a> include the following step:

> 1.  If <var>old effect</var> is not <code>null</code>, queue a task to call
>     any <a>custom effects</a> associated with <a>inclusive descendants</a> of
>     <var>old effect</var> with an <a>unresolved</a> <a>iteration progress</a>.
>
>     <div class="issue">
>       This is not quite right. If <var>old effect</var> is attached to another
>       animation in the same task then we should probably not do an extra
>       callback with <a>unresolved</a>.
>
>       The definition of when <a>custom effects</a> gets called needs to be
>       audited and probably rewritten.
>     </div>
</ins>

<h4 id='waiting-for-the-associated-effect'>Waiting for the associated effect</h4>

Replace:

>  An animation is ready at the first moment
>  where <em>both</em> of the following conditions are true:
>
>  * the user agent has completed any setup required
>      to begin the playback of the animation's [=associated effect=],
>      including rendering the first frame of any [=keyframe effect=].
>
>  * the animation is associated with a [=timeline=]
>      that is not [=inactive timeline|inactive=].

With:

>  An animation is <dfn>ready</dfn> at the first moment
>  where <em>all</em> of the following conditions are true:
>
> *   the user agent has completed any setup required to begin the playback of
>     each <a>inclusive descendant</a> of
>     the animation's <a>associated effect</a>
>     including rendering the first frame of any <a>keyframe
>     effect</a><ins cite="#amend-CE"> or executing any <a>custom effects</a> associated with an
>     <a>animation effect</a></ins>.
>
> *   the animation is associated with a [=timeline=]
>     that is not [=inactive timeline|inactive=].
>
> *   the animation's [=hold time=] or [=animation/start time=] is resolved.

Note: an animation is not <a>ready</a> while it has no [=animation/start time=] or [=hold time=].
For a [=scroll-driven animation=], the start-time is determined when [=event-loop|updating the timeline=] if |auto align start time| is true.


<h4 id='validating-a-css-numberish-time'>Validating a CSSNumberish time</h4>

The procedure to <dfn>validate a CSSNumberish time</dfn> for an input
value of <var>time</var> is based on the first condition that matches:

<dl class="switch">

    :   If <em>all</em> of the following conditions are true:

        *   The animation is associated with a [=progress-based timeline=], and
        *   <var>time</var> is not a CSSNumeric value with percent units:

    ::  <a>throw</a> a <span class="exceptionname">TypeError</span>.
    ::  return false;

    :   If <em>all</em> of the following conditions are true:

        *   The animation is not associated with a [=progress-based timeline=],
            and
        *   <var>time</var> is a CSSNumericValue, and
        *   the units of time are not <a type lt="<time>">duration units</a>:

    ::  <a>throw</a> a <span class="exceptionname">TypeError</span>.
    ::  return false.

    :   Otherwise
    ::  return true.

</dl>


<h4 id='setting-the-current-time-of-an-animation'>Setting the current time of an
Animation</h4>

The [=animation/current time=] of an animation can be set to a new value to
<em>seek</em> the animation.
The procedure for setting the current time is defined in two parts.

The procedure to <dfn>silently set the current time</dfn> of
an animation, <var>animation</var>, to <var>seek time</var> is as follows:

1.  If <var>seek time</var> is an <a>unresolved</a> time value,
    then perform the following steps.

    1.   If the [=animation/current time=] is <a lt=unresolved>resolved</a>, then
         <a>throw</a> a <span class=exceptionname>TypeError</span>.

    1.   Abort these steps.

1.  Let <var>valid seek time</var> be the result of running the
    [=validate a CSSNumberish time=] procedure with <var>seek time</var>
    as the input.

1.  If <var>valid seek time</var> is false, abort this procedure.

1.  Set |auto align start time| to false.

1.  Update either <var>animation</var>'s <a>hold time</a> or
    [=animation/start time=] as follows:

    <dl class="switch">

        :   If <em>any</em> of the following conditions are true:

            *   <var>animation</var>'s <a>hold time</a> is
                <a lt="unresolved">resolved</a>, or
            *   <var>animation</var>'s [=animation/start time=]
                is <a lt="unresolved">unresolved</a>, or
            *   <var>animation</var> has no associated <a>timeline</a> or
                the associated <a>timeline</a> is
                <a lt="inactive timeline">inactive</a>, or
            *   <var>animation</var>'s [=playback rate=] is 0,

        ::  1.  Set <var>animation</var>'s <a>hold time</a> to
                <var>seek time</var>.

        :   Otherwise,
        ::  Set <var>animation</var>'s [=animation/start time=] to the result of
            evaluating
            <code>|timeline time| - (|seek time| / [=playback rate=])</code>
            where <var>timeline time</var> is the current <a>time value</a>
            of <a>timeline</a> associated with <var>animation</var>.

    </dl>

1.  If <var>animation</var> has no associated <a>timeline</a> or the associated
    <a>timeline</a> is <a lt="inactive timeline">inactive</a>,
    make <var>animation</var>'s [=animation/start time=] <a>unresolved</a>.

    <p class=note>
      This preserves the invariant that when we don't have an active timeline it
      is only possible to set <em>either</em> the [=animation/start time=]
      <em>or</em> the animation's [=animation/current time=].
    </p>

1.  Make <a>animation</a>'s <var>previous current time</var> <a>unresolved</a>.


The procedure to <dfn>set the current time</dfn> of an animation,
<var>animation</var>, to <var>seek time</var> is as follows:

1.  Run the steps to <a>silently set the current time</a> of
    <var>animation</var> to <var>seek time</var>.
1.  If <var>animation</var> has a <a>pending pause task</a>, synchronously
    complete the pause operation by performing the following steps:
    1.  Set <var>animation</var>'s <a>hold time</a> to <var>seek time</var>.
    1.  [=Apply any pending playback rate=] to |animation|.
    1.  Make <var>animation</var>'s [=animation/start time=] <a>unresolved</a>.
    1.  Cancel the <a>pending pause task</a>.
    1.  <a lt="resolve a Promise">Resolve</a> <var>animation</var>'s
        <a>current ready promise</a> with <var>animation</var>.
1.  Run the procedure to <a>update an animation's finished state</a> for
    <var>animation</var> with the <var>did seek</var> flag set to true, and
    the <var>synchronously notify</var> flag set to false.

<h4 id='setting-the-start-time-of-an-animation'>Setting the start time of an
Animation</h4>


The procedure to <dfn>set the start time</dfn>
of <a>animation</a>, <var>animation</var>, to <var>new start time</var>,
is as follows:

1.  Let <var>valid start time</var> be the result of running the
    [=validate a CSSNumberish time=] procedure with
    <var>new start time</var> as the input.

1.  If <var>valid start time</var> is false, abort this procedure.

1.  Set |auto align start time| to false.

1.  Let <var>timeline time</var> be the current <a>time value</a> of the
    <a>timeline</a> that <var>animation</var> is associated with.
    If there is no <a>timeline</a> associated with <var>animation</var> or the
    associated timeline is <a lt="inactive timeline">inactive</a>,
    let the <var>timeline time</var> be <a>unresolved</a>.

1.  If <var>timeline time</var> is <a>unresolved</a> and <var>new start
    time</var> is <a lt="unresolved">resolved</a>, make <var>animation</var>'s
    <a>hold time</a> <a>unresolved</a>.

    <p class=note>
      This preserves the invariant that when we don't have an active timeline it
      is only possible to set <em>either</em> the [=animation/start time=]
      <em>or</em> the animation's [=animation/current time=].
    </p>

1.  Let <var>previous current time</var> be <var>animation</var>'s
    [=animation/current time=].

    Note: This is the [=animation/current time=] after applying the changes from
    the previous step which may cause the [=animation/current time=] to become
    <a>unresolved</a>.

1.  [=Apply any pending playback rate=] on |animation|.

1.  Set <var>animation</var>'s [=animation/start time=] to
    <var>new start time</var>.

1.  Update <var>animation</var>'s <a>hold time</a> based
    on the first matching condition from the following,

    <dl class="switch">

        :   If <var>new start time</var> is <a lt="unresolved">resolved</a>,
        ::  If <var>animation</var>'s [=playback rate=] is not zero,
            make <var>animation</var>'s <a>hold time</a> <a>unresolved</a>.

        :   Otherwise (<var>new start time</var> is <a>unresolved</a>),
        ::  1.  Set <var>animation</var>'s <a>hold time</a> to
                <var>previous current time</var> even if
                <var>previous current time</var> is <a>unresolved</a>.

    </dl>

1.  If <var>animation</var> has a <a>pending play task</a> or
    a <a>pending pause task</a>, cancel that task and
    <a lt="resolve a Promise">resolve</a> <var>animation</var>'s
    <a>current ready promise</a> with <var>animation</var>.

1.  Run the procedure to <a>update an animation's finished state</a> for
    <var>animation</var> with the <var>did seek</var> flag set to true, and
    the <var>synchronously notify</var> flag set to false.

<h4 id='playing-an-animation-section'>Playing an animation</h4>

The procedure to <dfn>play an animation</dfn>, <var>animation</var>, given
a flag <var>auto-rewind</var>, is as follows:

Note: The <var>auto-rewind</var> flag is provided for other specifications
that build on this model but do not require the rewinding behavior, such
as CSS Animations [[CSS-ANIMATIONS-1]].

1.  Let <var>aborted pause</var> be a boolean flag that is true if
    <var>animation</var> has a <a>pending pause task</a>, and false otherwise.
1.  Let <var>has pending ready promise</var> be a boolean flag that is
    initially false.
1.  Let <var>has finite timeline</var> be true if |animation| has an associated
    <a>timeline</a> that is not
    [=monotonically increasing timeline|monotonically increasing=].
1.  Let <var>previous current time</var> be the |animation|'s
    [=animation/current time=]
1.  Let <var>enable seek</var> be true if the <var>auto-rewind</var> flag is
    true and <var>has finite timeline</var> is false.
    Otherwise, initialize to false.
1.  Perform the steps corresponding to the <em>first</em> matching
    condition from the following, if any:

    <dl class="switch">

        :   If |animation|'s [=effective playback rate=] &gt; 0,
            <var>enable seek</var> is true and <em>either</em>
            <var>animation</var>'s:

            *   <var>previous current time</var> is <a>unresolved</a>, or
            *   <var>previous current time</var> &lt; zero, or
            *   <var>previous current time</var> &ge; <a>associated effect end</a>,

        ::  Set the |animation|'s <a>hold time</a> to zero.

        :   If |animation|'s [=effective playback rate=] &lt; 0,
            <var>enable seek</var> is true and <em>either</em>
            <var>animation</var>'s:

            *   <var>previous current time</var> is <a>unresolved</a>, or
            *   <var>previous current time</var> &le; zero, or
            *   <var>previous current time</var> &gt; <a>associated effect end</a>,
        ::
            <dl class="switch">

                :   If <a>associated effect end</a> is positive infinity,
                ::  <a>throw</a> an "{{InvalidStateError}}" {{DOMException}} and
                    abort these steps.
                :   Otherwise,
                ::  Set the |animation|'s <a>hold time</a> to the
                    |animation|'s <a>associated effect end</a>.

            </dl>

        :   If |animation|'s [=effective playback rate=] = 0 and |animation|'s
            [=animation/current time=] is [=unresolved=],

        ::  Set the |animation|'s <a>hold time</a> to zero.

    </dl>

1.  If |has finite timeline| and |previous current time| is unresolved:
    *   Set the flag |auto align start time| to true.

    Note: If play is called for a CSS animation during style update,
    the |animation|'s [=animation/start time=] cannot be reliably calculated until post layout since the start time is to align with the start or end of the animation range (depending on the [=playback rate=]).
    In this case, the animation is said to have an <dfn export for="animation">auto-aligned start
    time</dfn>,
    whereby the start time is automatically adjusted as needed to align the animation's progress to the animation range.

1.  If <var>animation</var>'s <a>hold time</a> is <a lt=unresolved>resolved</a>,
    let its [=animation/start time=] be <a>unresolved</a>.

1.  If <var>animation</var> has a <a>pending play task</a> or a
    <a>pending pause task</a>,

    1.  Cancel that task.
    1.  Set <var>has pending ready promise</var> to true.

1.  If the following three conditions are <em>all</em> satisfied:

    * |animation|'s [=hold time=] is [=unresolved=], and
    * |aborted pause| is false, and
    * |animation| does <em>not</em> have a [=pending playback rate=],

    abort this procedure.

1.  If <var>has pending ready promise</var> is false,
    let <var>animation</var>'s <a>current ready promise</a> be
    <a>a new promise</a> in the <a>relevant Realm</a> of <var>animation</var>.

1.  Schedule a task to run as soon as <var>animation</var> is <a>ready</a>.
    The task shall perform the following steps:

    1.  Assert that at least one of |animation|'s [=animation/start time=] or
        [=hold time=] is <a lt=unresolved>resolved</a>.

    1.  Let <var>ready time</var> be the <a>time value</a> of
        the <a>timeline</a> associated with <var>animation</var> at the moment
        when <var>animation</var> became <a>ready</a>.

    1.  Perform the steps corresponding to the first matching condition below,
        if any:

        <dl class="switch">

            :   If |animation|'s [=hold time=] is <a lt=unresolved>resolved</a>,

            ::  1.  [=Apply any pending playback rate=] on |animation|.

                1.  Let |new start time| be the result of evaluating
                    <code>|ready time| - [=hold time=] / [=playback rate=]</code>
                    for |animation|.
                    If the [=playback rate=] is zero, let
                    |new start time| be simply |ready time|.

                1.  Set the [=animation/start time=] of |animation| to
                    |new start time|.

                1.  If |animation|'s [=playback rate=] is not 0, make
                    |animation|'s [=hold time=] [=unresolved=].

            :   If |animation|'s [=animation/start time=] is resolved and
                |animation| has a [=pending playback rate=],

            ::  1.  Let |current time to match| be the result of evaluating
                    <code>(|ready time| - [=animation/start time=]) &times;
                    [=playback rate=]</code> for |animation|.

                1.  [=Apply any pending playback rate=] on |animation|.

                1.  If |animation|'s [=playback rate=] is zero, let
                    |animation|'s [=hold time=] be |current time to match|.

                1.  Let |new start time| be the result of evaluating
                    <code>|ready time| - |current time to match| /
                    [=playback rate=]</code> for |animation|.
                    If the [=playback rate=] is zero, let |new start time| be simply
                    |ready time|.

                1.  Set the [=animation/start time=] of |animation| to
                    |new start time|.

        </dl>

    1.  <a lt="resolve a promise">Resolve</a> <var>animation</var>'s <a>current
        ready promise</a> with <var>animation</var>.

    1.  Run the procedure to <a>update an animation's finished state</a> for
        <var>animation</var> with the <var>did seek</var> flag set to false,
        and the <var>synchronously notify</var> flag set to false.

        <div class="note">
            Note that the order of the above two steps is important
            since it means that an animation with zero-length <a>associated
            effect</a> will resolve its <a>current ready promise</a>
            before its <a>current finished promise</a>.
        </div>

    So long as the above task is scheduled but has yet to run,
    <var>animation</var> is described as having a
    <dfn>pending play task</dfn>.
    While the task is running, however, |animation| does <em>not</em> have
    a [=pending play task=].

    If a user agent determines that |animation| is immediately <a>ready</a>, it
    may schedule the above task as a microtask such that it runs at the next
    <a lt="perform a microtask checkpoint">microtask checkpoint</a>, but it must
    <em>not</em> perform the task synchronously.

    <div class="note">

    The above requirement to run the <a>pending play task</a> asynchronously
    ensures that code such as the following behaves consistently between
    implementations:

    <div class='example'><pre class='lang-javascript'>
    animation.play();
    animation.ready.then(
      () => { console.log('Playback commenced'); },
      () => { console.log('Playback was canceled'); }
    );
    // Suppose some condition requires playback to be canceled...
    animation.cancel();
    // "Playback was canceled" will be printed to the console.
    </pre></div>

    In the above code, were the [=pending play task=] run synchronously, the
    [=current ready promise=] would not be rejected.

    </div>

1.  Run the procedure to <a>update an animation's finished state</a> for
    <var>animation</var> with the <var>did seek</var> flag set to false, and
    the <var>synchronously notify</var> flag set to false.

Issue: The procedure to [=play an animation=] needs to include scheduling a task for
updating [=custom effects=].

<h5 id='auto-aligning-start-time'>Auto-aligning the start time</h5>

When attached to a non-monotonic timeline, the start time of the animation may be layout dependent.
In this case, we defer calculation of the start time until the timeline has been updated post
layout.
When updating timeline current time, the [=animation/start time=] of any attached animation is conditionally updated.
The procedure for <dfn export for="animation">calculating an auto-aligned start time</dfn> is as follows:

    1.  If the |auto-align start time| flag is false, abort this procedure.

    1.  If the timeline is inactive, abort this procedure.

    1.  If [=play state=] is idle, abort this procedure.

    1.  If [=play state=] is paused, and [=hold time=] is resolved,
        abort this procedure.

    1.  Let |start offset| be the resolved timeline time corresponding to the start of the [=animation attachment range=].
        In the case of view timelines, it requires a calculation based on the proportion of the cover range.

    1.  Let |end offset| be the resolved timeline time corresponding to the end of the [=animation attachment range=].
        In the case of view timelines, it requires a calculation based on the proportion of the cover range.

    1.  Set [=animation/start time=] to |start offset| if [=effective playback rate=] &ge; 0,
        and |end offset| otherwise.

    1.  Clear [=hold time=].


<h4 id='pausing-an-animation-section'>Pausing an animation</h4>

The procedure to [=pause an animation=] needs to refer not only to the
[=associated effect=] but also any descendants of the [=associated effect=].

<ins cite="#amend-CE">Likewise, the procedure to [=pause an animation=] needs to include scheduling
a task for updating [=custom effects=].</ins>

Update the constraints for setting the seek time to only apply when using a
monotonic timeline. A [=scroll-driven animation=] needs to defer setting the
hold time until the animation range has been computed.

Replace:

>   1. Let |seek time| be
>        a [=time value=] that is initially [=unresolved=].
>    1. Let |has finite timeline| be true
>        if |animation| has an associated [=timeline=]
>        that is not [=monotonically increasing timeline|monotonically increasing=].
>
>    1. If the |animation|'s [=animation/current time=] is [=unresolved=],
>        perform the steps according to the first matching condition below:
>
>        <dl class=switch>
>
>        : If |animation|'s [=playback rate=] is ≥ 0,
>        ::
>            Set |seek time| to zero.
>
>        : Otherwise,
>        ::
>            <dl class=switch>
>
>            : If [=associated effect end=] for |animation| is positive infinity,
>            ::
>                [=throw=] an "{{InvalidStateError}}" {{DOMException}}
>                and abort these steps.
>
>            : Otherwise,
>            ::
>                Set |seek time| to |animation|'s [=associated effect end=].
>
>            </dl>
>
>        </dl>
>
>    1. If |seek time| is [=unresolved|resolved=],
>
>            <div class=switch>
>
>            : If |has finite timeline| is true,
>            ::
>                Set |animation|'s [=animation/start time=] to |seek time|.
>
>            : Otherwise,
>            ::
>                Set |animation|'s [=animation/hold time=] to |seek time|.
>
>             </div>

with:

>    1. Let |has finite timeline| be true
>        if |animation| has an associated [=timeline=]
>        that is not [=monotonically increasing timeline|monotonically increasing=].
>
>    1. If the |animation|'s [=animation/current time=] is [=unresolved=] and
>       |has finite timeline| is false,
>        perform the steps according to the first matching condition below:
>
>        <dl class=switch>
>
>        : If |animation|'s [=playback rate=] is ≥ 0,
>        ::
>            Set [=hold time=] to zero.
>
>        : Otherwise,
>        ::
>            <dl class=switch>
>
>            : If [=associated effect end=] for |animation| is positive infinity,
>            ::
>                [=throw=] an "{{InvalidStateError}}" {{DOMException}}
>                and abort these steps.
>
>            : Otherwise,
>            ::
>                Set [=hold time=] to |animation|'s [=associated effect end=].
>
>            </dl>
>
>        </dl>
>
>    1. If |has finite timeline| is true,
>        and the |animation|'s [=animation/current time=] is [=unresolved=]
>       * Set the |auto align start time| flag to true.

Replace:

>   Schedule a task to be executed at the first possible moment where both of the following conditions are true:
>
>   * the user agent has performed any processing necessary to suspend the playback of animation’s associated effect, if any.
>
>   * the animation is associated with a timeline that is not inactive.

With:

>   Schedule a task to be executed at the first possible moment where all of the following conditions are true:
>
>   * the user agent has performed any processing necessary to suspend the playback of animation’s associated effect, if any.
>
>   * the animation is associated with a timeline that is not inactive.
>
>   * the animation has a resolved [=hold time=] or [=animation/start time=].

Note: A [=animation/start time=] is still required for a pause-pending animation if the |auto-align start time| flag is true to properly align to the animation range.
The [=animation/start time=] is set following the procedure for [=calculating an auto-aligned start time=].

<h4 id='canceling-an-animation-section'>Canceling an animation</h4>

<ins cite="#amend-CE">
Append the final step:

> 1.  Queue a task to call any <a>custom effects</a> associated with
>     <a>inclusive descendants</a> of
>     <var>animation</var>'s <a>associated effect</a>
>     with an <a>unresolved</a> iteration progress.
>
>     Issue: The procedures for calling custom effects need to be reworked.
>            Currently they probably involve calling too often for changes that
>            could be coalesced.
</ins>

<h4 id="speed-control">Speed control</h4>

<div class="informative-bg">

Add the following sentence:

>   Note that <a>animation effects</a> also have a
>   [=animation effect/playback rate=] associated
>   with them that behaves differently to that defined here.

</div>

### Calculating the overall progress of an animation ### {#the-overall-progress-of-an-animation}

  An animation's <dfn export for=animation>overallProgress</dfn> is the ratio of its
   [=animation/current time=] to its [=associated effect end=].

   <wpt>
    interfaces/Animation/scroll-timeline-overallProgress.tentative.html
    interfaces/Animation/overallProgress.html
   </wpt>

  <div algorithm="calculate animation overall progress">
    The [=animation/overallProgress=] of an [=animation=], |animation|, is calculated as follows:

    <dl class=switch>

    : If <em>any</em> of the following are true:
        * |animation| does not have an [=animation/associated effect=], or
        * |animation|'s [=animation/current time=] is an [=unresolved=] time value,
    ::
        |animation|'s [=animation/overallProgress=] is null.
    : If |animation|'s [=associated effect end=] is zero,
    ::
        : If |animation|'s [=animation/current time=] is negative,
        :: |animation|'s [=animation/overallProgress=] is zero.

        : Otherwise,
        :: |animation|'s [=animation/overallProgress=] is one.
    : If |animation|'s [=associated effect end=] is infinite,
    ::
        |animation|'s [=animation/overallProgress=] is zero.
    : Otherwise,
    ::
        <blockquote>
          <code><a for="animation">overallProgress</a> = min(max([=animation/current time=] / |animation|'s [=associated effect end=], 0), 1)
          </code>
        </blockquote>

    </dl>
  </div>


<h3 id="animation-effects">Animation effects</h3>

Adds the following text:

>     The <a>associated effect</a> of an <a>animation</a> is said to be
>     <dfn lt="directly associated with an animation">directly associated</dfn>
>     with that animation.
>
>     <a>Animation effects</a> can be combined together into a hierarchy using
>     <a>group effects</a> (see [[#grouping-and-synchronization]]).
>     Only the <a>root</a> <a>animation effect</a> of such a hierarchy can be
>     <a>directly associated with an animation</a>.
>     If an <a>animation effect</a> that has a <a>parent group</a>
>     is designated as the <a>associated effect</a> of an <a>animation</a>, the
>     <a>animation effect</a> is removed from its <a>parent group</a> before
>     being associated with the <a>animation</a>.
>
>     An <a>animation effect</a> is <dfn>associated with an animation</dfn> if
>     it is <a>directly associated with an animation</a> or if it has an
>     <a>ancestor</a> <a>group effect</a> that is <a>directly associated with an
>     animation</a>.

<h4 id="types-of-animation-effects">Types of animation effects</h4>

This specification defines two types of <a>animation effects</a>:

* <a>keyframe effects</a>, and
* <a>group effects</a>.


<h4 id="the-active-interval">The active interval</h4>

In this level of the specification the lower bound of the active interval is
defined as:

>     The lower bound of the <a>active interval</a> is determined by the
>     <a lt="animation effect start time">start time</a> of the
>     <a>animation effect</a> but may be shifted by a <a>start delay</a> on
>     the <a>animation effect</a>.

The subsequent diagram should also refer to the <em>animation effect start
time</em> as opposed to the <em>animation start time</em>.

Finally, the description of the [=end delay=] is updated to:

>     An <a>end delay</a> may also be specified but is primarily only of
>     use when sequencing animations such as by using a <a>sequence effect</a>.

The normative description is updated as follows:

>     The lower bound of the <a>active interval</a> is defined by the
>     combination of the animation effect's
>     <a lt="animation effect start time">start time</a> and <a>start delay</a>.
>
>     An <a>animation effect</a>'s
>     <dfn lt="animation effect start time">start time</dfn> is the moment at
>     which the <a>parent group</a>,
>     if any, has scheduled the <a>animation effect</a> to begin.
>     It is expressed in <a>inherited time</a>.
>
>     In most cases, including the case when the <a>animation effect</a> has
>     no <a>parent group</a>,
>     the <a lt="animation effect start time">start time</a> is zero.
>     The singular exception is <a>sequence effects</a> which set
>     the <a lt="animation effect start time">start times</a> of their
>     children as described in
>     [[#the-start-time-of-children-of-a-sequence-effect]].
>
>     In addition to the <a lt="animation effect start time">start
>     time</a>, an <a>animation effect</a> also has a <dfn>start delay</dfn>
>     which is an offset from the <a lt="animation effect start time">start
>     time</a>.
>
>     Unlike the <a lt="animation effect start time">start time</a> which
>     is determined by the <a>parent group</a>, the <a>start
>     delay</a> is a property of the <a>animation effect</a> itself.
>
>     The lower bound of the <a>active interval</a> of an <a>animation effect</a>,
>     expressed in <a lt="inherited time">inherited time space</a>,
>     is the sum of the <a lt="animation effect start time">start time</a>
>     and the <a>start delay</a>.
>
>     These definitions are incorporated in the calculation of the <a>local
>     time</a> (see [[#local-time-and-inherited-time]]) and <a>active time</a>.


And finally regarding the definition of the [=end time=]:

>     The <dfn>end time</dfn> of an <a>animation effect</a> is the result of
>     evaluating <code>max(<a lt="animation effect start time">start
>     time</a> + <a>start delay</a> + <a>active duration</a> + <a>end delay</a>,
>     0)</code>.


<h4 id="local-time-and-inherited-time">Local time and inherited time</h4>

This section is added.

<div class="informative-bg">

In Web Animations all times are relative to some point of reference.
These different points of reference produce different <em>time
spaces</em>.

This can be compared to coordinate spaces as used in computer
graphics.
The zero time of a time space is analogous to the origin of
a coordinate space.

Just as with coordinate spaces, time spaces can also be nested.
<a>Group effects</a> typically perform some
transformations on the <a>time values</a> they
receive from their <a lt="parent group">parent</a> or
<a>animation</a> before passing on the transformed <a>time values</a>
to their children.
Child <a>animation effects</a> then operate <em>within</em> that
transformed time space.

Children take the transformed time values from their
parent&mdash;called the <a>inherited time</a>&mdash;and add their
<a lt="animation effect start time">start time</a> to establish
their own <a lt="local time">local time space</a> as illustrated
below.

<figure>
  <img src="images/local-time.svg" width="600"
      alt="Inherited time and local time.">
  <figcaption>
    Inherited time and local time.<br>
    At time <var>t</var>, the <a>inherited time</a> is 2.5.<br>
    For <a>animation effect</a> (a) which has a <a
      lt="animation effect start time">start time</a> of 1, the
      <a>local time</a> is 1.5.<br>
    For <a>animation effect</a> (b) which has a <a
      lt="animation effect start time">start time</a> of 1
      and a <a>start delay</a> of 1,
      the <a>local time</a> is also 1.5
      since <a>local time</a> is based on an <a>animation effect</a>'s
      <a lt="animation effect start time">start time</a> only,
      and not on its <a>start delay</a>.
  </figcaption>
</figure>

</div>

For an <a>animation effect</a>, the <dfn>inherited time</dfn> at
a given moment is based on the first matching condition from the
following:

<dl class='switch'>

:   If the <a>animation effect</a> has a <a>parent group</a>,
::  the inherited time is the <a>parent group</a>'s current
    <a>transformed time</a>.
:   If the <a>animation effect</a> is <a>directly associated with
    an animation</a>,
::  the inherited time is the [=animation/current time=] of the
    <a>animation</a>.
:   Otherwise,
::  the inherited time is <a>unresolved</a>.

</dl>

The <dfn>local time</dfn> of an <a>animation effect</a> is the
<a>animation effect</a>'s <a>inherited time</a> minus its <a
lt="animation effect start time">start time</a>.
If the <a>inherited time</a> is <a>unresolved</a> then the local time
is also <a>unresolved</a>.


<h4 id="animation-effect-phases-and-states">Animation effect phases and
states</h4>

<div class="informative-bg"><em>This section is non-normative</em>

The non-normative description of the [=in play=] state includes the following
description;

>     This occurs when the <a>animation effect</a> <em>and all its
>     ancestors</em> are in the [=animation effect/active phase=].
>     <a>Animation effects</a> only &ldquo;move&rdquo; when
>     they are <a>in play</a>.
>
>     It is possible for an <a>animation effect</a> to be in the
>     [=animation effect/active phase=] but not <a>in play</a>.
>     For example, if an <a>animation effect</a> has a <a>parent
>     group</a> that causes the animation effect's <a>active
>     interval</a> to be clipped and both parent and child apply the
>     same <a>fill mode</a>, the child <a>animation effect</a> may be
>     effectively be snapshotted within the [=animation effect/active phase=]
>     despite no longer being <a>in play</a>.


Likewise for [=current=]

>     This will be the case if the <a>animation effect</a> is <a>in
>     play</a> or in its [=animation effect/before phase=], <em>or</em> it has an
>     ancestor for which this is true thereby opening up the
>     possibility that this <a>animation effect</a> might play again
>     (e.g. due to repeating).

</div>

The normative definition of <dfn>in play</dfn> includes the following condition:

> 2.  the <a>animation effect</a> has a <a>parent group</a> that
>     is <a>in play</a> or else the <a>animation effect</a> is
>     <a>directly associated with an animation</a> that is not
>     [=play state/finished=].

In place of:

> 2.  the <a>animation effect</a> is <a>associated with an animation</a> that is
>     not [=play state/finished=].


The normative definition of <dfn>current</dfn> adds the following
condition:

> *   the <a>animation effect</a> has a <a>parent group</a> and the
>     <a>parent group</a> is <a>current</a>.

Add the following definitions to the list used in determining the phase of
an <a>animation effect</a>.

:   <dfn>at progress timeline boundary</dfn>
::  Determined using the following procedure:

    1.  If <em>any</em> of the following conditions are true:
        *   the associated <a>animation</a>'s timeline is not a
            [=progress-based timeline=], or
        *   the associated <a>animation</a>'s [=timeline duration=] is
            unresolved or zero, or
        *   the <a>animation</a>'s [=playback rate=] is zero

        return false

    1.  Let <var>effective start time</var> be the <a>animation</a>'s
        [=animation/start time=] if resolved, or zero otherwise.

    1.  Set <var>unlimited current time</var> based on the first matching
        condition:

        :   [=animation/start time=] is resolved:

        ::  <code>(<var>timeline time</var> - [=animation/start time=])
            &times; [=playback rate=]</code>

        :   Otherwise

        ::  <code>animation's [=animation/current time=]</code>

    1.  Let <var>effective timeline time</var> be
        <code>|unlimited current time| / <a>animation</a>'s
        [=playback rate=] + |effective start time|</code>

    1.  Let <var>effective timeline progress</var> be
        <code><var>effective timeline time</var> / [=timeline duration=]</code>

    1.  If <var>effective timeline progress</var> is 0 or 1, return true,
        otherwise false.

Issue: This procedure is not strictly correct for a paused
animation if the <a>animation</a>'s current time is explicitly set, as this can
introduce a lead or lag, between the <a>timeline</a>'s current time and
<a>animation</a>'s current time.

Issue: This procedure can likely be simplified, and instead determine if at a
scrolling boundary regardless of playback rate or start time. The surprising
behavior that this is trying to prevent is an animation becoming inactive
precisely at the scroll limit, alleviating the need for set a fill-mode with
a ScrollTimeline. Checking if timeline [=timeline/current time=] is
0 or timeline duration may be sufficient.


Replace:

> An <a>animation effect</a> is in the before phase if the animation effect's
> <a>local time</a> is not <a>unresolved</a> and <em>either</em> of the
> following conditions are met:
>
> 1.  the <a>local time</a> is less than the <a>before-active boundary time</a>,
>     <em>or</em>
> 1.  the <a>animation direction</a> is "backwards" and the <a>local
>     time</a> is equal to the <a>before-active boundary time</a>.

with:

> An <a>animation effect</a> is in the before phase if the animation effect's
> <a>local time</a> is not <a>unresolved</a> and <em>either</em> of the
> following conditions are met:
>
> 1.  the <a>local time</a> is less than the <a>before-active boundary time</a>,
>     <em>or</em>
> 1.  the <a>animation direction</a> is "backwards" and the <a>local
>     time</a> is equal to the <a>before-active boundary time</a>  and not
>     <a>at progress timeline boundary</a>.


Replace:

> An <a>animation effect</a> is in the after phase if the animation
> effect's <a>local time</a> is not <a>unresolved</a> and <em>either</em> of the
> following conditions are met:
>
> 1.  the <a>local time</a> is greater than the <a>active-after boundary
>     time</a>, <em>or</em>
> 1.  the <a>animation direction</a> is "forwards" and the <a>local
>     time</a> is equal to the <a>active-after boundary time</a>.

with:

> An <a>animation effect</a> is in the after phase if the animation
> effect's <a>local time</a> is not <a>unresolved</a> and <em>either</em> of the
> following conditions are met:
>
> 1.  the <a>local time</a> is greater than the <a>active-after boundary
>     time</a>, <em>or</em>
> 1.  the <a>animation direction</a> is "forwards" and the <a>local
>     time</a> is equal to the <a>active-after boundary time</a> and not
>     <a>at progress timeline boundary</a>.


<h4 id="fill-modes">Fill modes</h4>

<div class='informative-bg'><em>This section is non-normative</em>

The description of the forwards fill mode is updated from:

>     When the animation effect is in the [=animation effect/after phase=],
>     &hellip;

to:

>     When the animation effect is in the [=animation effect/after phase=],
>     or when the animation effect is in the [=animation effect/active phase=]
>     but an <a>ancestor</a> is in its [=animation effect/after phase=],
>     &hellip;

The description of the backwards fill mode is updated from:

>     When the animation effect is in the [=animation effect/before phase=],
>     &hellip;

to:

>     When the animation effect is in the [=animation effect/before phase=],
>     or when the animation effect is in the [=animation effect/active phase=]
>     but an <a>ancestor</a> is in its [=animation effect/before phase=],
>     &hellip;

The description of the both fill mode is updated from:

>     When the animation effect &hellip;

to:

>     When the animation effect or an <a>ancestor</a> &hellip;

(twice).


<div class="issue">
Currently <a>timing functions</a> that generate results outside the
range [0, 1] will behave unexpectedly when applied to group
effects, as children will increase iterations or enter into fill mode
rather than continuing to extrapolate along their defined behavior
(which is what they would do if the timing function applied to them
directly).

To fix this it is possible we will wish to introduce &lsquo;overflow&rsquo;
fill modes that respond to time values larger than or smaller than the
active time range by extrapolating rather than filling.

See <a
href='http://lists.w3.org/Archives/Public/public-fx/2013AprJun/0184.html'>section
15 (Overflowing fill) of minuted discussion from Tokyo 2013 F2F</a>.
</div>

</div>

<h3 id="repeating">Repeating</h3>

<h4 id="iteration-intervals">Iteration intervals</h4>

After:

>     The length of a single iteration is called the iteration duration.

Add:

>     The initial <a>iteration duration</a> of an animation effect is simply its
>     <a>intrinsic iteration duration</a>.
>     The <dfn>intrinsic iteration duration</dfn> is calculated from the first
>     matching condition from below:
>
> <dl class="switch">
>     :   If the animation effect is a <a>group effect</a>,
>     ::  Follow the procedure in
>         [[#the-intrinsic-iteration-duration-of-a-group-effect]]
>
>     :   If the animation effect is a <a>sequence effect</a>,
>     ::  Follow the procedure in
>         [[#the-intrinsic-iteration-duration-of-a-sequence-effect]]
>
>     :   If [=timeline duration=] is unresolved or <a>iteration count</a> is zero,
>     ::  Return 0
>
>     :   Otherwise
>     ::  Return <code>(100% - <a>start delay</a> - <a>end delay</a>) /
>         <a>iteration count</a></code>
>
>         Note: Presently start and end delays are zero until such time as percentage
>         based delays are supported.
> </dl>
>
>     The <a>iteration duration</a> of an <a>animation effect</a> may be set
>     by the author to represent a value other than the <a>intrinsic
>     iteration duration</a>.

<h4 id="iteration-time-space">Iteration time space</h4>

<div class="informative-bg"><em>This section is non-normative</em>

The first few paragraphs of this section are replaced with:

>     We have already encountered different time spaces in describing
>     <a>local time</a> and <a>inherited time</a> (see
>     [[#local-time-and-inherited-time]]).
>     Repetition introduces yet another time space: the iteration time
>     space.

</div>

<h4 id="interval-timing">Interval timing</h4>

<div class="informative-bg"><em>This section is non-normative</em>

The description is updated as follows:

>     In the examples below, for the repeated effect, at local time 1s,
>     the iteration time is 0.
>     For the sequenced effects, at inherited time 1s, only effect B will be
>     <a>in play</a>; there is no overlap.

And likewise the figure is updated as follows:

<figure>
  <img src="images/endpoint-exclusive-timing.svg" width="600"
    alt="Illustration of end-point exclusive timing.">
  <figcaption>
    Illustration of end-point exclusive timing. For both repeated and
    sequenced animation effects there is no overlap at the boundaries
    between intervals.
  </figcaption>
</figure>

</div>

<h3 id="animation-effect-speed-control">Animation effect speed control</h3>

(This section is added.)

Like <a>animations</a>, <a>animation effects</a> also have a <dfn
for="animation effect" lt="playback rate">playback rate</dfn> parameter.
The [=animation effect/playback rate=] of
an <a>animation effect</a> is a finite real number that acts as
a multiplier when calculating the <a>animation effect</a>'s <a>transformed
time</a> from its <a>local time</a>.

The effect of setting the [=animation effect/playback rate=]
of an <a>animation effect</a> differs from the
setting the [=playback rate=] on an animation.
Its behavior is defined in the timing calculations given in
[[#core-animation-effect-calculations]].

<div class="informative-bg"><em>This section is non-normative</em>

In summary, the behavior of the [=animation effect/playback rate=]
of an <a>animation effect</a> is as follows:

*   The [=animation effect/playback rate=] is
    applied only to the <a>active interval</a> of an <a>animation
    effect</a> and not to the time while it is <a
    lt="start delay">delayed</a> or <a lt="fill mode">filling</a>.

*   Setting a negative [=animation effect/playback rate=]
    on an <a>animation effect</a> causes the <a>animation
    effect</a> to begin at the end of its <a>active interval</a>.
    This differs from the [=playback rate=] on an <a>animation</a>.
    Setting a [=playback rate=] on
    an <a>animation</a> to a negative value at a moment before the
    <a>animation</a>'s <a>associated effect</a> has begun, will result in the
    <a>associated effect</a> never playing.

*   Setting the [=animation effect/playback rate=]
    of an <a>animation effect</a> affects the calculated value of
    the <a>active duration</a> (see [[#calculating-the-active-duration]]).

*   Changing the [=animation effect/playback rate=] of an
    <a>animation effect</a> whose <a>local time</a> is
    within its <a>active interval</a> will cause it to jump.
    This is because the <a>active duration</a> will be updated but the
    <a>local time</a> will not.

    Furthermore, if other <a>animation effects</a> depend on the
    <a>animation effect</a>'s <a>active duration</a>, such as sibling
    <a>animation effect</a> in a <a>sequence effect</a>, they
    too may jump as a result of setting the <a>animation effect</a>'s
    [=animation effect/playback rate=].

    For runtime speed control the [=playback rate=] of the
    <a>animation</a> should be used.

</div>

<h3 id="core-animation-effect-calculations">Core animation effect
calculations</h3>

<h4 id="animation-effect-calculations-overview">Overview</h4>

<div class="informative-bg"><em>This section is non-normative</em>

Update description from:

>     At the core of the Web Animations timing model is the process that
>     takes a <a>local time</a> value and converts it to an <a>iteration
>     progress</a>.

to:

>     At the core of the Web Animations timing model is the process that
>     takes an <a>inherited time</a> value
>     and converts it to an <a>iteration progress</a>.

Update diagram to show animation effect playback rate:

<figure>
  <img src="images/active-duration-calculation.svg" width="650"
    alt="Calculation of the active duration.">
  <figcaption>
    Calculation of the <a>active duration</a> is based on
    multiplying the <a>iteration duration</a> by the
    <a>iteration count</a> and then dividing by the
    [=animation effect/playback rate=].
  </figcaption>
</figure>

Update:

>     Having established the <a>active duration</a>, the process for
>     transforming an <a>animation effect</a>'s
>     <a>local time</a> into its
>     <a>transformed progress</a> (<a>iteration progress</a>) is illustrated
>     below.

to:

>     Having established the <a>active duration</a>, the process for
>     transforming an <a>animation effect</a>'s
>     <a>inherited time</a> into its
>     <a>transformed progress</a> (<a>iteration progress</a>) is illustrated
>     below.

Update the time calculations diagram as follows:

<figure>
  <img src="images/time-calculations.svg" width="650"
    alt="An overview of timing model calculations.">
  <figcaption>
    An overview of timing model calculations.<br>
    (1) The <a>inherited time</a> is converted into a <a>local time</a> by
    incorporating the <a lt="animation effect start time">start time</a>.<br>
    (2) The <a>local time</a> is converted into an <a>active time</a> by
    incorporating the <a>start
    delay</a>.<br>
    (3) The <a>active time</a> is divided by the <a>iteration duration</a>
    incorporating also the <a>iteration start</a>
    property and the [=animation effect/playback rate=] property
    to produce the <a>overall progress</a>.<br>
    (4) The <a>overall progress</a> time is then converted to an offset
    within a single iteration: the <a>simple iteration progress</a>.<br>
    (5) The <a>simple iteration progress</a> is converted into a <a>directed
    progress</a> by incorporating the <a>playback direction</a>.<br>
    (6) Finally, a timing function is applied to the <a>directed
    progress</a> to produce the <a>transformed progress</a>.
  </figcaption>
</figure>

Update:

>     The first step, calculating the <a>local time</a> is described in
>     Local time.

to:

>     The first step, calculating the <a>local time</a> is described in
>     [[#local-time-and-inherited-time]].

</div>


<h4 id="calculating-the-active-duration">Calculating the active duration</h4>

Updated to:

>     In order to calculate the <a>active duration</a> we first define the
>     <a>repeated duration</a> as follows:
>
>     <blockquote>
>       <dfn>repeated duration</dfn> =
>         <code><a>iteration duration</a> &times;
>               <a>iteration count</a></code>
>         <p>
>           If either the <a>iteration duration</a> or <a>iteration count</a>
>           are zero, the <a>repeated duration</a> is zero.
>         </p>
>         <p class="note">
>           This clarification is needed since the result of infinity
>           multiplied by zero is undefined according to IEEE 754-2008.
>         </p>
>     </blockquote>
>
>     The <dfn>active duration</dfn> is calculated according to the following
>     steps:
>
>     1.  If the [=animation effect/playback rate=] is
>           zero, return <code>Infinity</code>.
>     2.  Otherwise, return <code><a>repeated duration</a> /
>           abs([=animation effect/playback rate=])</code>.

<h4 id="transforming-the-local-time">Transforming the local time</h4>

<h5 id="calculating-the-active-time">Calculating the active time</h5>

Update the qualification on the definition of the [=active time=] to include
reference to the [=parent group=]:

>     However, it is only defined when the <a>animation effect</a> should
>     produce an output and hence depends on its <a>fill mode</a> and
>     phase as well as the phase of its <a>parent group</a>, if any,
>     as follows,


Update the definition to include the additional steps to for each phase:

> <dl class="switch">
>     :   If the animation effect is in the [=animation effect/before phase=],
>     ::  The result depends on the first matching condition from the
>         following,
>
>         <dl class="switch">
>
>             :   If the animation effect has a <a>parent group</a>
>                 and that parent group is in the <a>after
>                 phase</a>,
>             ::  Return an <a>unresolved</a> <a>time value</a>.
>
>         </dl>
>
>         &hellip;
>
>     :   If the animation effect is in the [=animation effect/active phase=],
>     ::  The result depends on the first matching condition from the
>         following,
>
>         <dl class="switch">
>
>             :   If the animation effect has a <a>parent group</a>
>                 and that parent group is in the <a>before
>                 phase</a>, and the <a>fill mode</a> of this animation effect
>                 is <span class="prop-value">none</span> or <span
>                 class="prop-value">forwards</span>,
>             ::  Return an <a>unresolved</a> <a>time value</a>.
>             :   If the animation effect has a <a>parent group</a>
>                 and that parent group is in the <a>after
>                 phase</a>, and the <a>fill mode</a> of this animation effect
>                 is <span class="prop-value">none</span> or <span
>                 class="prop-value">backwards</span>,
>             ::  Return an <a>unresolved</a> <a>time value</a>.
>             :   Otherwise,
>             ::  Return the result of evaluating
>                 <code><a>local time</a> - <a>start delay</a></code>.
>
>         </dl>
>
>     :   If the animation effect is in the [=animation effect/after phase=],
>     ::  The result depends on the first matching condition from the
>         following,
>
>         <dl class="switch">
>
>             :   If the animation effect has a <a>parent group</a>
>                 and that parent group is in the <a>before
>                 phase</a>,
>             ::  Return an <a>unresolved</a> <a>time value</a>.
>
>         </dl>
>
>         &hellip;
>
>     :   Otherwise (the <a>local time</a> is <a>unresolved</a>),
>     ::  Return an <a>unresolved</a> <a>time value</a>.
> </dl>

<h5 id="calculating-the-overall-progress">Calculating the overall progress</h5>

In the definition of the [=overall progress=] update the &ldquo;Otherwise&rdquo;
branch of the definition of the |initial progress| as:

>     :   Otherwise,
>     ::  Calculate the <var>overall progress</var> following the first
>         matching condition from below:
>
>         <dl class="switch">
>
>             :   If the [=animation effect/playback rate=] is less than zero,
>             ::  Let <var>overall progress</var> be
>                 <code>(<a>active time</a> - <a>active duration</a>)
>                 &times; [=animation effect/playback rate=]
>                 / <a>iteration duration</a></code>.
>
>             :   If the [=animation effect/playback rate=] is zero,
>             ::  Let <var>overall progress</var> be zero.
>
>             :   Otherwise,
>             ::  Let <var>overall progress</var> be
>                 <code>(<a>active time</a> &times;
>                 [=animation effect/playback rate=]) /
>                 <a>iteration duration</a></code>.
>
>         </dl>

<h3 id="time-transformations">Time transformations</h3>

<div class="issue">
Currently, the set of <a>timing functions</a> allowed on
a <a>group effect</a> is not restricted.
This has raised concern about complexity of implementation and also
complexity of behavior with regards to fill modes.
As a result, allowing the full set of timing functions on group
effects is considered <strong>at risk</strong>.

Alternatives are to either restrict timing functions on group
effects to the <a>linear timing function</a> or to a set of
&ldquo;simple&rdquo; timing functions that have properties that
alleviate some of the concerns with the more complex timing
functions.

See <a
href="http://lists.w3.org/Archives/Public/public-fx/2013JulSep/0076.html">section
2 of the discussion from August 2013</a>.</div>


<h4 id="calculating-the-transformed-progress">Calculating the transformed progress</h4>

Replace the second step as the calculation of the <a spec=css-easing>before
flag</a> (to accommodate the effect playback rate):

>     1.  If <em>either</em> the <var>current direction</var> is <span
>         class="prop-value">forwards</span> or the
>         [=animation effect/playback rate=] &ge; 0
>         (but <em>not</em> when both conditions are true),
>         let <var>going forwards</var> be true, otherwise it is false.


<h4 id="calculating-the-transformed-time">Calculating the transformed time</h4>

(This section is added.)

The <dfn>transformed time</dfn> of an animation effect is simply the
<a>transformed progress</a> multiplied by the <a>iteration duration</a>.

If the <a>transformed progress</a> is <a>unresolved</a>, then the <a>transformed
time</a> is also <a>unresolved</a>.

If the <a>transformed progress</a> is zero and the <a>iteration duration</a>
is infinity, then the <a>transformed time</a> is zero.


<h3 id="grouping-and-synchronization">Grouping and synchronization</h3>

<div class="informative-bg"><em>This section is non-normative</em>

While it is possible to set the timing properties of <a>animation
effects</a> individually, it is often useful to synchronize <a>animation
effects</a> so that they share common timing properties and maintain
their temporal relationship.  This is achieved using an <a>group
effect</a>.

A simple example is illustrated below.

<figure>
  <img src="images/grouping-delay.svg" width="800"
       alt="Using groups to share common timing properties.">
  <figcaption>
    Using groups to share common timing properties.<br>
    (a) Shows setting a delay of 5 seconds on individual animations.<br>
    (b) Produces the same effect by setting the delay on the group.
  </figcaption>
</figure>

When a <a>group effect</a> is <a>directly associated with
an animation</a>, the <a>animation effect</a> associated with the
<a>group effect</a> can be seeked, paused, and stopped as a unit.

</div>

A <dfn>group effect</dfn> is a type of <a>animation effect</a> that
contains an ordered sequence of zero or more <a>animation effects</a>
known as <dfn lt="child effect">child effects</dfn>.

At a given moment, an <a>animation effect</a> may be a <a>child effect</a>
of at most one <a>group effect</a> known as the <dfn>parent group</dfn>.
The <a>parent group</a> cannot be the same <a>animation
effect</a> as the <a>child effect</a> itself.

By nesting <a>group effects</a> it is possible to create hierarchical
tree structures.
The following terms are used to describe the parts and properties of
such structures and are defined in [[!DOM]]:

*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-order">
    tree order</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-root">
    root</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-ancestor">
    ancestor</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-descendant">
    descendant</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-inclusive-ancestor">
    inclusive ancestor</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-inclusive-descendant">
    inclusive descendant</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-next-sibling">
    next sibling</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-previous-sibling">
    previous sibling</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-first-child">
    first child</a></dfn>
*   <dfn><a href="https://www.w3.org/TR/dom/#concept-tree-last-child">
    last child</a></dfn>

Note: in applying these definitions to <a>animation effects</a>, the
    term <em>parent</em> refers exclusively to a <a>parent group</a> and does
    <em>not</em> include the <a>animation</a> which with
    an <a>animation effect</a> may be <a
    lt="directly associated with an animation">directly associated</a>
    despite the fact that conceptually the <a>animation</a> acts as a parent
    time source.

The temporal relationship between a <a>child effect</a> and its
<a>parent group</a> is incorporated in the definition of
<a>inherited time</a> (see [[#local-time-and-inherited-time]]).

<h4 id="relationship-of-group-time-to-child-time">Relationship of group time to child time</h4>

<div class="informative-bg"><em>This section is non-normative</em>

The timing of the children of a <a>group effect</a> is based on
the timing of the group.
Specifically, times for the children are based on the parent's
<a>transformed time</a>.
With regards to repetition, this means the children operate
<em>inside</em> an iteration of the parent.

For example, if a <a>group effect</a> has an <a>iteration
count</a> of 2, then the children of the group will all play twice
since they effectively play <em>inside</em> the group's iterations.

<figure>
  <img src="images/grouping-repetition.svg" width="600"
    alt="The effect of multiple iterations on the children of a group.">
  <figcaption>
    Since children of a <a>group effect</a> base their timing on the
    group's <a>transformed time</a>, when the group repeats, the
    children play again.
  </figcaption>
</figure>

Note that even in this case, the child <a>animation effects</a> still
have only <em>one</em> <a>active interval</a>.
However, as a result of the parent's timing, the <a>active
interval</a> is played twice.

If an <a>iteration count</a> is specified for the children of a group
as well as for the group itself, the effect is as if the <a>iteration
count</a> of the group was multiplied with the <a>iteration count</a>
of the children.

<figure>
  <img src="images/grouping-repetition-and-animation-repetition.svg"
    width="600" alt="Iteration counts are multiplicative.">
  <figcaption>
    Specifying an <a>iteration count</a> of 2 on a <a>group effect</a>
    and an <a>iteration count</a> of 3 on one of its children results in
    that child playing 6 times.
  </figcaption>
</figure>

A further result of the children of a <a>group effect</a> basing
their timing on the group's <a>transformed time</a> is that they
cannot animate outside of the group's <a>active interval</a>.
This is because the <a>transformed time</a> of a group will not
change outside its <a>active interval</a>.
This allows groups to <em>clip</em> the playback of their children.

<figure>
  <img src="images/grouping-clipping.svg" width="600"
    alt="Groups clip the active interval of contained children.">
  <figcaption>
    In the first instance, an <a>animation effect</a> has a negative delay
    and an infinite <a>iteration count</a>.<br>
    However, when a similar <a>animation effect</a> is placed inside
    a <a>group effect</a> with a specified <a>iteration duration</a>
    it has the effect of clipping the child <a>animation effect</a>'s
    <a>active interval</a>.
  </figcaption>
</figure>

Some further consequences of <a>group effect</a> children basing
their timing on their parent group's <a>transformed time</a> are:

*   Setting the [=animation effect/playback rate=] of a <a>group effect</a>
    will speed up or slow down all children.

*   Changing the <a>playback direction</a> of a <a>group effect</a>
    will change the direction of all children.

*   Applying a <a>timing function</a> to a <a>group effect</a> will
    affect the progress of all children.

</div>

<h4 id="the-start-time-of-children-of-a-group-effect">The start time of
  children of a group effect</h4>

The <a lt="animation effect start time">start time</a> of a <a>child
effect</a> of a <a>group effect</a> is zero.

Note that specific types of <a>group effects</a> may override
this definition to provide other kinds of synchronization behavior.

<h4 id="the-intrinsic-iteration-duration-of-a-group-effect">The intrinsic
  iteration duration of a group effect</h4>

The <a>intrinsic iteration duration</a> of a <a>group
effect</a> is based on the time when the last <a>child effect</a> completes
its <a>active interval</a> and depends on the
number of <a>child effects</a> as follows.

<dl class="switch">

    :   If the group has no <a>child effects</a>,
    ::  the <a>intrinsic iteration duration</a> is zero.

    :   Otherwise,
    ::

        1.  Let <var>maximum end time</var> be the maximum value
            after calculating the <a>end time</a> of each <a>child
            effect</a> in the group. The <a>end time</a> may be
            expressed as a time or a percentage (in the case of a
            [=progress-based timeline=]). In the event of mixed time and
            percentage <a>end time</a>s, the largest time based value
            equates to 100% for the purpose of time scaling.
        2.  The <a>intrinsic iteration duration</a> is the result of
            evaluating <code>max(0, <var>maximum end
            time</var>)</code>.

</dl>

This definition of the <a>intrinsic iteration duration</a> may be
overridden by specific types of <a>group effects</a>.

<h4 id="sequence-effects">Sequence effects</h4>

<div class="informative-bg"><em>This section is non-normative</em>

Specific types of <a>group effects</a> can be used to provide
different kinds of synchronization behavior for their children.
This specification defines one additional type of <a>group
effect</a>: a <a>sequence effect</a>.
<a>Sequence effects</a> arrange the start times of their
children so that they run one at a time, in turn.

Compare the two arrangements illustrated below:

<figure>
  <img src="images/grouping-types.svg" width="600"
    alt="Group effects and sequence effects.">
  <figcaption>
    Group effects and sequence effects.<br>
    (a) is a regular <a>group effect</a> where all the children run
        simultaneously.<br>
    (b) is a <a>sequence effect</a> where the children run in turn.
  </figcaption>
</figure>

Since <a>group effects</a> can also contain other <a>group
effects</a>, complex synchronization is possible by combining
different types of groups as illustrated below.

<figure>
  <img src="images/grouping-nesting.svg" width="600"
    alt="Nesting of group effects.">
  <figcaption>
    A <a>sequence effect</a> that contains a regular <a>group
    effect</a> as a child.<br>
    The <a>group effect</a> waits for the previous child of the
    <a>sequence effect</a> to finish, and then the children of the
    <a>group effect</a> play simultaneously.
    After they have finished the next child of the <a>sequence
    effect</a> plays.
  </figcaption>
</figure>

</div>

A <dfn>sequence effect</dfn> is a type of <a>group effect</a>
that schedules its <a>child effects</a> such that they play in
turn following their order in the group.
This sequencing is achieved by adjusting the <a
lt="animation effect start time">start time</a> of each <a>child
effect</a> in the group.

<h5 id="the-start-time-of-children-of-a-sequence-effect">The start time of
  children of a sequence effect</h5>

The <a lt="animation effect start time">start time</a> of
a <a>child effect</a> of a <a>sequence effect</a> is the
<a>end time</a> of the child's <a>previous sibling</a>.
If the child has no <a>previous sibling</a> the start time is zero.

<div class="note">
When the <a>active duration</a> is positive infinity the behavior
for calculating the <a>end time</a> of an <a>animation effect</a>
and the <a lt="animation effect start time">start time</a> of
subsequent children follows the usual behavior defined by IEEE
754-2008.
As a result, if any of the children of a <a>sequence
effect</a> has an infinite <a>active duration</a>, any children
that occur later in the sequence will not play.

Similarly, the above definition does not restrict
<a lt="animation effect start time">start times</a> to positive
values and hence some children may not play due to a negative
<a>start delay</a> on children that occur earlier in the group
since their <a>active interval</a> may end before the group's <a
lt="animation effect start time">start time</a>.
</div>

<div class="informative-bg"><em>This section is non-normative</em>

Because the start of the <a>active interval</a> is based on the
sum of an <a>animation effect</a>'s <a
lt="animation effect start time">start time</a> <em>and</em>
<a>start delay</a>, the <a>active intervals</a> of
children of a <a>sequence effect</a> need not run in strict
sequence but can be shifted back and forth by using the <a>start
delay</a> as shown in the following diagram.

<figure>
  <img src="images/sequence-groups-and-start-delays.svg" width="600"
    alt="Using negative start delays to overlap children of seq
        groups">
  <figcaption>
    Example of using the <a>start delay</a> on children of a
    <a>sequence effect</a> to shift their timing so that they
    overlap (a negative delay) or are spaced apart (a positive delay).
  </figcaption>
</figure>

A negative <a>start delay</a> can be used to cause the <a>active
interval</a> of two children to overlap.
Note that the <a>start delay</a> affects the <a
lt="animation effect start time">start time</a> of subsequent
children in the group.

</div>

<h5 id="the-intrinsic-iteration-duration-of-a-sequence-effect">The intrinsic
  iteration duration of a sequence effect</h5>

The <a>intrinsic iteration duration</a> of a <a>sequence
effect</a> is equivalent to the <a
lt="animation effect start time">start time</a> of a hypothetical
<a>child effect</a> appended to the group's children
calculated according to the definition in <a
href="#the-start-time-of-children-of-a-sequence-effect"
section></a> unless that produces a negative value, in
which case the <a>intrinsic iteration duration</a> is zero.

As a result, if the <a>sequence effect</a> has no <a>child
effects</a> the <a>intrinsic iteration duration</a> will be
zero.


<!-- End of timing model -->


<h2 id="animation-model">Animation model</h2>

<h3 id="animation-types">Animation types</h3>

<h4 id="not-animatable-section">Not animatable</h4>

Update the description of an effect that targets a non-animated property as:

>     An <a>animation effect</a> that targets a property that is
>     <a>not animatable</a> will still exhibit the
>     usual behavior for an <a>animation effect</a> such as
>     occupying time in a <a>sequence effect</a> and
>     delaying the fulfillment of an <a>animation</a>'s <a>current finished
>     promise</a>.

<h3 id="keyframe-effects">Keyframe Effects</h3>

<h4 id="the-effect-value-of-a-keyframe-animation-effect">The effect value of a keyframe effect</h4>

The procedure for computing the <a>effect value</a> of a single property
referenced by a <a>keyframe effect</a> as one of its <a>target properties</a>,
for a given <var ignore=''>iteration progress</var>, <var>current iteration</var> and
<var ignore=''>underlying value</var> is amended by inserting the following step
after the step to apply the keyframe effect composite mode.

12. For each <var>keyframe</var> in <var ignore=''>interval endpoints</var>:

    1.  (As in web-animations-1).

    1.  If this <a>keyframe effect</a> has an <a>iteration
        composite operation</a> of <a
        lt="iteration composite operation accumulate">accumulate</a>,
        apply the following step <var>current iteration</var> times:

        *   replace the property value of <var>target property</var>
            on <var>keyframe</var> with the result of combining the
            property value on the final keyframe in <var ignore=''>property-specific
            keyframes</var> (<var>V</var><sub>a</sub>) with the
            property value on <var>keyframe</var>
            (<var>V</var><sub>b</sub>) using the <a
            lt="value accumulation">accumulation procedure</a>
            defined for <var>target property</var>.

        Note: The order of arguments here is important. In the case where
              the animation type of the target property does not define a
              procedure for accumulation or addition, the default definition
              for these procedures result in <var>V</var><sub>b</sub> being
              returned. When performing iteration composition on properties
              that do not support accumulation, the result should be the
              initial property value of <var>target property</var> on
              <var>keyframe</var>, hence we make this
              <var>V</var><sub>b</sub> in the above step.

<h3 id="combining-effects">Combining effects</h3>

<h4 id="the-effect-stack">The effect stack</h4>

The procedure for sorting effects appends the following step:

>     1.  Sort <var>A</var> and <var>B</var> in <a>tree order</a>.
>         (By this point, <var>A</var> and <var>B</var> must have the
>         same <a>animation</a> since otherwise the order would have been
>         resolved in the previous step.)

Issue: The "associated animation of an animation effect" definition used when
sorting animation effects needs to be revised to use the [=associated with an
animation=] definition introduced in this level of the specification so that it
correctly handles group effects.

<h3 id="effect-accumulation-section">Effect accumulation</h3>

Similar to the compositing performed between <a>effect values</a>
(see [[web-animations-1#effect-composition]]), the <dfn>iteration composite operation</dfn>
determines how values are combined between successive iterations of
the same <a>keyframe effect</a>.

This specification defines two <a>iteration composite operations</a>
as follows:

:   <dfn lt="iteration composite operation replace">replace</dfn>
::  Each successive iteration is calculated independently of previous
    iterations.
:   <dfn lt="iteration composite operation accumulate">accumulate
::  Successive iterations of the animation are <a
    lt="value accumulation">accumulated</a> with the
    final value of the previous iteration.

    The application of the <a>iteration composite operation</a> is
    incorporated in the calculation of the <a>effect value</a> in <a
    href="#the-effect-value-of-a-keyframe-animation-effect"
    section></a>.

<ins cite="#amend-CE">
<h3 id="custom-effects">Custom effects</h3>

(This section is added.)

Issue: This whole feature needs to be revisited. The current thinking is that
       rather than having custom effects, we should simply have an
       <code>onupdate</code> callback on each <a>animation effect</a>.  That
       would allow, for example, augmenting an existing effect with a function
       that performs logging or triggers additional actions at certain times.
       With the current arrangement, doing this would require adding a parent
       group just to achieve this.

<div class='informative-bg'><em>This section is non-normative</em>

In some situations the <a>animation
effects</a> provided by Web Animations may be insufficient.
For example, the <a>animation effects</a>
defined here are only able to target certain CSS properties.
They are unable, therefore, to modify the <a
href="https://www.w3.org/TR/SVG11/struct.html#__svg__SVGSVGElement__currentScale"><code>currentScale</code></a>
property of an SVG element to smoothly zoom the viewport without
affecting the document content.

In such cases, where the provided <a>animation
effects</a> do not provide needed functionality, an effect defined by
script may be used.
Such <a>custom effects</a> receive an <a>iteration
progress</a> and [=current iteration index|current iteration=] from the timing model and
are responsible for producing an effect corresponding to the specified
time.

Using an effect defined in script it is possible to animate not only
otherwise un-animatable attributes and properties, but potentially
anything that is accessible via script, including even producing audio
or creating vibrations.

For example, using a <a>custom effect</a> that draws to a <{canvas}>
element, it is possible to produce a complex animated effect
featuring patterns that may be difficult to create using CSS or
SVG.
Compared to using <a
href="https://www.w3.org/TR/animation-timing/">Timing control for
script-based animations</a>,
this approach ensures the animation is frame-rate
independent and can be paused, reversed, eased with timing effects,
accelerated, synchronized with other animations, and be controlled
in the same manner as any other Web Animations animation without any
additional programming.

</div>

A <dfn>custom effect</dfn> is an author-defined programming callback
that is passed timing information as part of the [=update animations and send
events=] procedure.

Issue: It needs to be called whenever timing properties are updated too, right?

<h4 id="updating-custom-effects">Sampling custom effects</h4>

<a>Custom effects</a> are called for each referencing <a>animation effect</a>
when the [=update animations and send events=] procedure is performed
(henceforth referred to simple as an <em>update</em>) based on
the following criteria.

1.  If, on the previous update, the <a>animation effect</a>
    referencing the <a>custom effect</a>:

    *   was <a>in effect</a>, and
    *   referenced this same <a>custom effect</a>, and
    *   had a different [=effect target/target element=]

    Call the callback passing an <a>unresolved</a> <a>iteration progress</a>
    and the [=effect target/target element=] from the previous update as
    parameters to the callback.
2.  Call the callback for the current [=effect target/target element=] based on
    the first matching condition from the following:

    <dl class="switch">

        :   If the <a>animation effect</a> referencing the custom
            effect is not <a>in effect</a> but was <a>in effect</a> in the previous
            update,
        ::  Call the callback passing an <a>unresolved</a> <a>iteration progress</a>
            and the current [=effect target/target element=] as parameters to
            the callback.
        :   Otherwise, if the <a>animation effect</a> referencing
            the custom effect:
        ::

            *   <strong>is <a>in effect</a>, and</strong>
            *   <strong>was not <a>in effect</a> in the previous update, or
                was <a>in effect</a> but with a different <a>iteration
                progress</a> or [=current iteration index|current iteration=],</strong>

        ::  Call the callback passing with the referencing
            <a>animation effect</a>'s current <a>iteration
            progress</a> and [=effect target/target element=] as parameters to
            the callback.

    </dl>

<div class="issue">
There may be use cases where an action needs to be triggered at
a specific point in an animation tree.
In many cases this can be achieved by inserting a custom effect with
a step-start easing that spans the period during which the action
should be triggered.
However, this can impose additional layout requirements on the
content which might be cumbersome to accommodate.

Some alternatives under consideration:

*   Additional calling conditions could be defined to accommodate
    zero-width custom effects. For example, it could be required
    that the callback be called if (given infinite precision) there
    was a time between the previous and current update times that
    aligned with the custom effect.
*   Instead of adding special calling conditions to custom effects,
    a new type of animation effect, Trigger, could be introduced. The
    trigger would only ever act as a zero-width custom effect as
    described above, its constructor would take a callback function,
    but not require a target or timing. It could also specify other
    calling conventions, for example whether it should only trigger
    in a specific playback direction.

</div>

<h4 id="execution-order-of-custom-effects">Execution order of custom effects</h4>

Since <a>custom effects</a>, unlike <a
lt="animation effect">animation effects</a>, are not limited to
a single <a>target property</a>, the steps for assessing their
order of execution differs from <a>animation effects</a>.f

<a>Custom effects</a> are executed after all
<a>animation effects</a> have completed and
applied their result to their targets (see Applying the composited result).

Issue: Need to define this more precisely.
    Are styles flushed?
    Presumably they are.
    Can we suspend reflow for the duration of executing the script-based
    animation effects and just do it once afterwards?

Within the set of <a>custom effects</a>, the
order of execution is the same as that defined for <a>animation effects</a> in
[[#the-effect-stack]].
Items sorted earlier are executed before those sorted later.

</ins>

Animation Triggers {#triggers}
-----------------

### Overview ### {#triggers-overview}

An <dfn export>animation trigger</dfn> is used to control the playback
of its associated [=animation=] for time-driven animations.
Like animations, [=animation triggers=] are associated with a [=timeline=]
and are attached to an [=animation attachment range|attachment range=].

Issue: Should there be any effect of triggers on scroll-driven animations?

### Animation Trigger Internal State ### {#trigger-state}

An [=animation trigger=] |trigger|
has an internal boolean <dfn export>did trigger</dfn> flag, which is initially false,
and an internal <dfn export lt="animation trigger state">state</dfn> with
these possible values:

<dl dfn-for="animation trigger state">
  <dt><dfn>idle</dfn>
    <dd>
      The [=animation effect=] associated |animation| remains in
      its [=animation effect/before phase=] and stays at zero [=animation/current time=].

    <dt><dfn>primary</dfn>
    <dd>
      When switched to this value the [=animation trigger state/primary=] behavior type defined by |type| is applied to |animation|.

    <dt><dfn>inverse</dfn>
    <dd>
      When switched to this value the [=animation trigger state/inverse=] behavior type defined by |type| is applied to |animation|.
</dl>

The values of [=animation trigger state|state=] and [=did trigger=] are
updated by the [=updating animation trigger state=] procedure, described below.

Issue: Do we need a formal resolution on the spec of the idle state?

### Animation Trigger Behavior ### {#trigger-behaviors}

An [=animation trigger=] has an associated <dfn export>animation trigger behavior</dfn>
which, along with [=did trigger=] and [=animation trigger state|state=],
determines the [=animation trigger|trigger=]'s effect on its
associated [=animation=]'s playback.

The [=animation effect=] associated with an [=animation trigger=]
in the [=animation trigger state/idle=] state
remains in its [=animation effect/before phase=] and
stays at zero [=animation/current time=],
regardless of the trigger's [=did trigger=] flag or [=animation trigger state|state=].
Otherwise, the effect on playback is as follows for
given values of <var ignore=''>behavior</var>:

<dl>
  <dt>''animation-trigger-behavior/once''</dt>
  <dd>
    <dl class=switch>
      : If [=animation trigger state|state=] is [=animation trigger state/primary=],
      :: the effect is to [=play an animation|trigger=] the associated animation.

      : Otherwise,
      :: the trigger has no effect.
  </dl>
  </dd>

  <dt>''animation-trigger-behavior/repeat''</dt>
  <dd>
    <dl class=switch>
      : If [=animation trigger state|state=] is [=animation trigger state/primary=],
      :: the effect is to [=play an animation|trigger=] the associated animation.

      : If [=animation trigger state|state=] is [=animation trigger state/inverse=],
      :: the effect is to reset the associated [=animation effect=] back to
         its [=animation effect/before phase=] and its associated [=animation=]’s
         [=animation/start time=] to zero.
  </dl>
  </dd>

  <dt>''animation-trigger-behavior/alternate''
  <dd>
    <dl class=switch>
      : If [=animation trigger state|state=] is [=animation trigger state/primary=]
        and [=did trigger=] is false,
      :: the effect is to [=play an animation|trigger=] the associated animation.

      : If [=animation trigger state|state=] is [=animation trigger state/primary=]
        and [=did trigger=] is true,
      :: the effect is to [=reverse an animation|reverse=] the associated animation.

      : If [=animation trigger state|state=] is [=animation trigger state/inverse=]
      :: the effect is to [=reverse an animation|reverse=] the associated animation.
  </dl>
  </dd>

  <dt>''animation-trigger-behavior/state''
  <dd>
    <dl class=switch>
      : If [=animation trigger state|state=] is [=animation trigger state/primary=]
      :: the effect is to [=play an animation|trigger or resume=] the associated animation.

      : If [=animation trigger state|state=] is [=animation trigger state/inverse=]
      :: the effect is to [=pause an animation|pause=] the associated animation.
  </dl>
  </dd>
</dl>

Issue: Do we need a proper definition for "behavior types primary/inverse"?

### Animation Trigger Active Interval ### {#trigger-active-interval}

Each [=animation trigger=] defines an <dfn for="animation trigger">active interval</dfn>
and only one such interval. This interval is the segment of the timeline’s progress
during which the trigger’s [=animation trigger state|state=] is set to [=animation trigger state/primary=].

### Animation Trigger Ranges ### {#trigger-ranges}

An [=animation trigger=] has two [=animation attachment range|ranges=],
the <dfn>default range</dfn> and the <dfn>exit range</dfn>.
The [=exit range=] is used to replace [=default range=] and extend
the [=animation trigger/active interval|active interval=].

Issue: Should we specify that that exit range’s edges can only be equal to or greater than the default range’s edges?

Depending on its most recent [=animation trigger state|state=] |state|, the
[=animation trigger/active interval|active interval=] is defined as follows:

<dl class=switch>
  : If |state| is [=animation trigger state/primary=],
  ::
    Then |interval| is set to [=exit range=].

  : Otherwise,
  ::
    |interval| is [=default range=].

</dl>

### Setting a Trigger of an Animation ### {#setting-animation-trigger}

  <div algorithm="Setting a Trigger of an Animation">
    The procedure of <dfn>setting a trigger of an animation</dfn> |animation|,
    to |new trigger|, is as follows:

    1. Let |old trigger| be the current [=animation trigger=] of |animation|, if any.

    1. If |new trigger| is the same object as |old trigger|, abort this procedure.

    1. Let the [=animation trigger|trigger=] of |animation| be |new trigger|.

    1. Run the procedure to [=updating animation trigger state=] for |new trigger|.
  </div>

### Setting the timeline of an Animation Trigger ### {#setting-trigger-timeline}

  <div algorithm="setting timeline of animation trigger">
    The procedure of <dfn>setting timeline of animation trigger</dfn> |trigger|,
    to |new timeline| which may be <code>null</code>, is as follows:

    1. Let |old timeline| be the current value of |trigger|.{{AnimationTrigger/timeline}}, if any.

    1. If |new timeline| is the same object as |old timeline|, abort this procedure.

    1. Set |trigger|.{{AnimationTrigger/timeline}} to |new timeline|.

    1. Run the [=updating animation trigger state=] procedure for |trigger|.
  </div>

### Updating Animation Trigger State ### {#updating-trigger-state}

<div algorithm="updating animation trigger state">
  The procedure of <dfn>updating animation trigger state</dfn>
  of an [=animation trigger=] |trigger| is as follows:

  1. Let |did trigger| be the current value of |trigger|'s [=did trigger=] flag.

  1. Let |behavior| be |trigger|'s associated [=animation trigger behavior=].

  1. Set |trigger|.[=animation trigger state|state=] as follows:
    <dl class=switch>
      : If |trigger|’s [=local time=] is [=unresolved=],
      ::
        Then abort this procedure.

      : Otherwise,
      ::

        <dl class=switch>
          : If |behavior| is ''animation-trigger-behavior/once'' and |did trigger| is true,
          ::
            Then abort this procedure.

          : Otherwise,
          ::

            <dl class=switch>
              : If |trigger| is inside its [=animation trigger/active interval|active interval=],
              ::
                  1. Set |trigger|.[=animation trigger state|state=] to [=animation trigger state/primary=].
                  1. Set |trigger|.[=did trigger=] to true.

              : Otherwise,
              ::

                <dl class=switch>
                  : If |did trigger| is true,
                  ::
                      Set |trigger|.[=animation trigger state|state=] to [=animation trigger state/inverse=].

                </dl>

            </dl>

        </dl>

    </dl>

</div>

Issue: Should it be the correct/expected behavior for the "did trigger" flag to reset to false if the timeline becomes idle again?

<!-- End of animation model -->

<h2 id="programming-interface">Programming interface</h2>

<h3 id="the-animationtimeline-interface">The <code>AnimationTimeline</code> interface</h3>

<pre class="idl">
[Exposed=Window]
partial interface AnimationTimeline {
    readonly attribute CSSNumberish? currentTime;
    readonly attribute CSSNumberish? duration;
    Animation play (optional AnimationEffect? effect = null);
};
</pre>

<div class='attributes'>

Update the attribute type for currentTime.

:   <dfn attribute for=AnimationTimeline>currentTime</dfn>
::  Returns the [=timeline/current time=] for this timeline or
    <code>null</code> if this timeline is
    <a lt="inactive timeline">inactive</a>.  The value is expressed as a
    percentage for a [=progress-based timeline=], or as a double in
    milliseconds otherwise.

:   <dfn attribute for=AnimationTimeline>duration</dfn>
::  Returns the <a lt="timeline duration">duration</a> for this timeline.

<wpt>
    interfaces/DocumentTimeline/duration.tentative.html
</wpt>

</div>

<div class='methods'>

:   <dfn method for=AnimationTimeline lt='play()'>
    Animation play(optional AnimationEffect? effect = null)</dfn>
::  Creates a new {{Animation}} object associated with
    this <a>timeline</a> that begins playback as soon as it is
    <a>ready</a>.

    If <var>effect</var> is specified, it will be used as the animation's
    <a>associated effect</a>.

    Issue: It has been suggested this method be renamed, or even removed
    (see <a
    href="https://github.com/w3ctag/spec-reviews/blob/master/2013/10/Web%20Animations.md#issue-play-method">TAG
    feedback</a>).

    Issue: Need to define the start behavior when <var>effect</var> is null.

    The new {{Animation}} object is created using the
    {{Animation()}} constructor passing this
    {{AnimationTimeline}} object as the <var>timeline</var> parameter and
    <var>effect</var> as the <var>effect</var> parameter.

    Following construction of the {{Animation}} object, the procedure to
    <a>play an animation</a> is performed on the newly constructed object
    with the <var>auto-rewind</var> flag set to true.

    <div class='parameters'>

    :   <dfn argument for="AnimationTimeline/play(effect)"
         lt="effect">effect</dfn>
    ::  the <a>associated effect</a> to assign to the newly-created
        {{Animation}} object.

    </div>

</div>

<h3 id="the-animation-interface">The <code>Animation</code> interface</h3>

Update the startTime and currentTime of the Animation interface, and add
rangeStart, rangeEnd, and overallProgress as follows:

<pre class="idl">
[Exposed=Window]
partial interface Animation {
    attribute CSSNumberish?       startTime;
    attribute CSSNumberish?       currentTime;
    attribute AnimationTrigger?   trigger;
    attribute (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart;
    attribute (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd;
    readonly attribute double? overallProgress;
};
</pre>

Add or update attribute descriptions as follows:

<div class='attributes'>

:   <dfn attribute for=Animation>startTime</dfn>
::  Update the description as:

    > The [=animation/start time=] of this animation. When associated with a
    > [=progress-based timeline=], [=animation/start time=] must be returned as
    > a {{CSSNumericValue}} with percent units. Otherwise
    > [=animation/start time=] must be returned as a double value, representing
    > the time in units of milliseconds. Setting this attribute updates the
    > [=animation/start time=] using the procedure to <a>set the start time</a>
    > of this object to the new value.

:   <dfn attribute for=Animation>currentTime</dfn>
::  update the description as:

    > The [=animation/current time=] of this animation. When associated with a
    > [=progress-based timeline=], [=animation/current time=] must be returned
    > as a {{CSSNumericValue}}. with percent units.
    > Otherwise [=animation/current time=] must be returned as a double value,
    > representing the time in units of milliseconds.
    > Setting this attribute follows the procedure to
    > <a>set the current time</a> of this object to the new value.

Add:

> :   <dfn attribute for=Animation>rangeStart</dfn>
> ::  Specifies the start of the <a>animation</a>’s [=animation attachment range=].
>     Setting the attribute follows the same rules as the KeyframeAnimationOption
>     <a href="#dom-keyframeanimationoptions-rangestart">rangeStart</a>.
>     When reading the attribute, the returned value is either a
>     {{TimelineRangeOffset}} or the {{DOMString}} "normal".
>
> :  <dfn attribute for=Animation>rangeEnd</dfn>
> ::  Specifies the end of the <a>animation</a>’s [=animation attachment range=].
>     Setting the attribute follows the same rules as the KeyframeAnimationOption
>      <a href="#dom-keyframeanimationoptions-rangeend">rangeEnd</a>.
>     When reading the attribute, the returned value is either a
>     {{TimelineRangeOffset}} or the {{DOMString}} "normal".
>
> :  <dfn attribute for=Animation>overallProgress</dfn>
> ::  Specifies the [=animation/overallProgress=] of the <a>animation</a> as a proportion of its
>     [=associated effect end=].
>
> :  <dfn attribute for=Animation>trigger</dfn>
> ::  Specifies the <a>animation</a>’s associated [=animation trigger=].
>     Setting the attribute follows the same rules as the KeyframeAnimationOption
>     <a href="#dom-keyframeanimationoptions-trigger">trigger</a>.
</div>

<h3 id="the-animationeffect-interface">The <code>AnimationEffect</code> interface</h3>

<pre class='idl'>
[Exposed=Window]
partial interface AnimationEffect {
    // Timing hierarchy
    readonly attribute GroupEffect?     parent;
    readonly attribute AnimationEffect? previousSibling;
    readonly attribute AnimationEffect? nextSibling;

    undefined before (AnimationEffect... effects);
    undefined after (AnimationEffect... effects);
    undefined replace (AnimationEffect... effects);
    undefined remove ();
};
</pre>

<div class='attributes'>

:   <dfn method for=AnimationEffect>getComputedTiming()</dfn>
::  The description of the <code>duration</code> attribute of the object
    needs to indicate that if <code>timing.duration</code>
    is the string <code>auto</code>, this attribute will return
    the current calculated value of the
    <a>intrinsic iteration duration</a>, which may be a expressed as a double
    representing the duration in milliseconds or a percentage when the effect
    is associated with a [=progress-based timeline=].

:   <dfn attribute for=AnimationEffect>parent</dfn>
::  The <a>parent group</a> of this <a>animation effect</a> or
    <code>null</code> if this <a>animation effect</a> does not have
    a <a>parent group</a>.

    Issue: Should this be <code>parentGroup</code>?

:   <dfn attribute for=AnimationEffect>previousSibling</dfn>
::  The <a>previous sibling</a> of this <a>animation effect</a>.
:   <dfn attribute for=AnimationEffect>nextSibling</dfn>
::  The <a>next sibling</a> of this <a>animation effect</a>.

</div>

<div class='methods'>

:   <dfn method for=AnimationEffect lt='before()'>
    undefined before (AnimationEffect... effects)</dfn>
::  Inserts <var>effects</var> before this <a>animation effect</a>.

    1.  If there is no <a>parent group</a>, abort these steps.
    2.  If any of the <a>animation effects</a> in <var>effects</var> is an
        <a>inclusive ancestor</a> of this <a>animation effect</a>,
        <a>throw</a> a <span class=exceptionname>HierarchyRequestError</span>
        exception and abort these steps.
    3.  <a lt="insert children">Insert</a> <var>effects</var> before
        this <a>animation effect</a>.

    <div class='note'>
        Note that this definition precludes the following usage since
        <code>effect</code> is an <a>inclusive ancestor</a> of itself:

        <pre class="lang-javascript">
effect.before(effect); // throws HierarchyRequestError</pre>
    </div>

:   <dfn method for=AnimationEffect lt='after()'>
    undefined after(AnimationEffect... effects)</dfn>
::  Inserts <var>effects</var> after this <a>animation effect</a>.

    1.  If there is no <a>parent group</a>, abort these steps.
    2.  If any of the <a>animation effects</a> in <var>effects</var> is an
        <a>inclusive ancestor</a> of this <a>animation effect</a>,
        <a>throw</a> a <span class=exceptionname>HierarchyRequestError</span>
        exception and abort these steps.
    3.  Let <var>reference child</var> be the <a
        lt="next sibling not included">next sibling of
        this animation effect not in <var>effects</var></a>.
    4.  <a lt="insert children">Insert</a> <var>effects</var> before
        <var>reference child</var>.

:   <dfn method for=AnimationEffect lt='replace()'>
    undefined replace(AnimationEffect... effects)</dfn>
::  Replaces this {{AnimationEffect}} with the passed in
    <var>effects</var>.

    1.  If there is no <a>parent group</a>, abort these steps.
    2.  If any of the <a>animation effects</a> in <var>effects</var> is an
        <a>inclusive ancestor</a> of the <a>parent group</a>
        <a>throw</a> a <span class=exceptionname>HierarchyRequestError</span>
        exception and abort these steps.
    3.  Let <var>reference child</var> be the <a
        lt="next sibling not included">next sibling of
        this animation effect not in <var>effects</var></a>.
    4.  <a lt="remove an animation effect">Remove</a> this
        <a>animation effect</a> from its <a>parent group</a>.
    5.  <a lt="insert children">Insert</a> <var>effects</var> before
        <var>reference child</var>.

:   <dfn method for=AnimationEffect lt='remove()'>undefined remove()</dfn>
::  <a lt="remove an animation effect">Removes</a> this <a>animation
    effect</a> from its <a>parent group</a> or <a>animation</a>.

</div>

Issue: The <code>remove()</code> method can be used to remove an effect from
either its parent group or animation. Should we keep it in level 1 and define it
simply as removing the animation from its animation?


<h3 id="the-effecttiming-dictionaries">The <code>EffectTiming</code> and <code>OptionalEffectTiming</code> dictionaries</h3>

<pre class="idl">
partial dictionary EffectTiming {
    double delay;
    double endDelay;
    double playbackRate = 1.0;
    (unrestricted double or CSSNumericValue or DOMString) duration = "auto";
};

partial dictionary OptionalEffectTiming {
    double playbackRate;
};
</pre>

Note: In this version of the spec, duration is not settable as a
CSSNumericValue; however, duration may be returned as a CSSNumericValue when
resolving the duration in {{AnimationEffect/getComputedTiming()}}. Future
versions of the spec may enable setting the duration as a CSSNumeric value,
where the unit is a valid time unit or percent.

<div class="attributes">

:   <dfn dict-member for=EffectTiming>delay</dfn><dfn dict-member
    for=OptionalEffectTiming lt=delay></dfn>
::  Update the description as:

    > The <dfn>specified start delay</dfn> which represents the number of
    > milliseconds from an <a>animation effect</a>'s
    > <a lt="animation effect start time">start time</a> to the start of the
    > <a>active interval</a>.
    > The |specified start delay| is converted to a <a>start delay</a> following
    > the [=normalize specified timing=] procedure.

:   <dfn dict-member for=EffectTiming>endDelay</dfn><dfn dict-member
    for=OptionalEffectTiming lt=endDelay></dfn>
::  Update the description as:

    > The <dfn>specified end delay</dfn> which represents the number of milliseconds
    > from the end of an <a>animation effect</a>'s <a>active interval</a>
    > until the <a lt='animation effect start time'>start time</a> of any
    > <a>animation effect</a> that may follow, for example, in a
    > <a>sequence effect</a>. The |specified end delay| is converted to an
    > <a>end delay</a> following the [=normalize specified timing=] procedure.

:   <dfn dict-member for=EffectTiming>duration</dfn><dfn dict-member
    for=OptionalEffectTiming lt=duration></dfn>
::  Update the description as:

    > The <dfn>specified iteration duration</dfn> which is a real number greater
    > than or equal to zero (including positive infinity) representing the
    > time taken in milliseconds to complete a single iteration of the
    > <a>animation effect</a>, or the string value <code>auto</code> to indicate
    > that the <a>iteration duration</a> reflects the animation effect's
    > <a>intrinsic iteration duration</a>. The |specified iteration duration| is
    > converted to an <a>iteration duration</a> following the
    > [=normalize specified timing=] procedure.

:   <dfn dict-member for=EffectTiming>playbackRate</dfn>
::  The <a>animation effect</a>'s [=animation effect/playback rate=]
    property which is a multiplier applied to the <a>local time</a> potentially
    causing the effect to run at a different rate to its natural speed.

</div>

<h3 id="updating-animationeffect-timing">Updating the timing of an
AnimationEffect</h3>

Replace:

> Assign each member that <a for=map>exists</a> in |input| to the corresponding
>    timing property of |effect| as follows:
>
>    *   {{EffectTiming/delay}} &rarr; [=start delay=]
>    *   {{EffectTiming/endDelay}} &rarr; [=end delay=]
>    *   {{EffectTiming/fill}} &rarr; [=fill mode=]
>    *   {{EffectTiming/iterationStart}} &rarr; [=iteration start=]
>    *   {{EffectTiming/iterations}} &rarr; [=iteration count=]
>    *   {{EffectTiming/duration}} &rarr; [=iteration duration=]
>    *   {{EffectTiming/direction}} &rarr; [=playback direction=]
>    *   {{EffectTiming/easing}} &rarr; [=timing function=]

with:

> Assign each member that <a for=map>exists</a> in |input| to the corresponding
>    timing property of |effect| as follows:
>
>    *   {{EffectTiming/delay}} &rarr; [=specified start delay=]
>    *   {{EffectTiming/endDelay}} &rarr; [=specified end delay=]
>    *   {{EffectTiming/fill}} &rarr; [=fill mode=]
>    *   {{EffectTiming/iterationStart}} &rarr; [=iteration start=]
>    *   {{EffectTiming/iterations}} &rarr; [=iteration count=]
>    *   {{EffectTiming/duration}} &rarr; [=specified iteration duration=]
>    *   {{EffectTiming/direction}} &rarr; [=playback direction=]
>    *   {{EffectTiming/easing}} &rarr; [=timing function=]

Add:

>    Follow the procedure to [=normalize specified timing=].
>
>    <p class="note">
>    Timing properties may also be updated due to a style change. Any change to
>    a CSS animation property that affects timing requires rerunning the
>    procedure to [=normalize specified timing=].
>    </p>

<h3 id="the-computedeffecttiming-dictionary">The
  <code>ComputedEffectTiming</code> dictionary</h3>

<pre class='idl'>
partial dictionary ComputedEffectTiming {
    CSSNumberish         startTime;
    CSSNumberish         endTime;
    CSSNumberish         activeDuration;
    CSSNumberish?        localTime;
};
</pre>

<div class='members'>

:   <dfn dict-member for=ComputedEffectTiming>startTime</dfn>
::  The <a lt='animation effect start time'>start time</a> of this
    <a>animation effect</a> expressed as a percentage if associated with a
    [=progress-based timeline=], or as a double in milliseconds otherwise.

    This is the time at which the <a>parent group</a>, if
    any, has scheduled this child to run within its <a
    lt="transformed time">transformed time space</a>, that is, the
    <a>animation effect</a>'s <a lt="inherited time">inherited time
    space</a>.

    The start of the <a>active interval</a> is based on the sum of
    the <a lt="animation effect start time">start time</a> and
    <a>start delay</a>.

:   <dfn dict-member for=ComputedEffectTiming>endTime</dfn>
::  Update the description as:

    > The <a>end time</a> of the <a>animation effect</a> in
    > <a lt="inherited time">inherited timespace</a>. The value is expressed as
    > a percentage if associated with a [=progress-based timeline=], or as a
    > double in milliseconds otherwise.
    > This corresponds to the end of the <a>animation effect</a>'s active
    > interval plus any <a>end delay</a>.

:   <dfn dict-member for=ComputedEffectTiming>activeDuration</dfn>
::  Update the description as:

    > The <a>active duration</a> of this <a>animation effect</a> expressed
    > as a percentage if associated with a [=progress-based timeline=], or as a
    > double in milliseconds otherwise.

:   <dfn dict-member for=ComputedEffectTiming>localTime</dfn>
::  Update the second paragraph as:

    > This will be <code>null</code> if this
    > <a>animation effect</a> is not
    > <a>associated with an animation</a> or if it has a
    > <a>parent group</a> that is not <a>in effect</a>.

    Append:

    > The value is expressed as a percentage if associated with a
    > [=progress-based timeline=], or as a double in milliseconds otherwise.

</div>

<h4 id="the-fillmode-enumeration">The <code>FillMode</code> enumeration</h4>

<pre class='idl'>
enum FillMode { "none", "forwards", "backwards", "both", "auto" };
</pre>

:   <code>auto</code>
::  Update the description as:

    > Fill backwards and forwards when applied to a
    > {{GroupEffect}} and no fill when applied to a
    > {{KeyframeEffect!!interface}}.

<h3 id="the-groupeffect-interface">The <code>GroupEffect</code> interface</h3>

(This section is added.)

<a>Group effects</a> are represented by the {{GroupEffect}}
interface.

<pre class='idl'>
[Exposed=Window]
interface GroupEffect {
  constructor(sequence&lt;AnimationEffect>? children,
              optional (unrestricted double or EffectTiming) timing = {});

  readonly attribute AnimationNodeList  children;
  readonly attribute AnimationEffect?   firstChild;
  readonly attribute AnimationEffect?   lastChild;
  GroupEffect clone ();

  undefined prepend (AnimationEffect... effects);
  undefined append (AnimationEffect... effects);
};
</pre>

<div class="constructors">

:   <dfn constructor for=GroupEffect lt="GroupEffect(children, timing)">GroupEffect ()</dfn>
::  Creates a new {{GroupEffect}} object using the following
    procedure:

    1.  Create a new {{GroupEffect}} object, <var>group</var>.

    1.  Let <var>timing input</var> be the result of applying the
        procedure to <a>process a timing argument</a> to
        {{GroupEffect/GroupEffect(children, timing)/timing}}.

    1.  Let <var>timing input</var> be the result corresponding to the first
        matching condition from below.

        :   If <var>options</var> is an {{EffectTiming}} object,
        ::  Let <var>timing input</var> be <var>options</var>.

        :   Otherwise (if <var>options</var> is a <code>double</code>),
        ::  Let <var>timing input</var> be a new
            {{EffectTiming}} object with all members set to their default values
            and {{EffectTiming/duration}} set to <var>options</var>.

    1.  Call the procedure to [=update the timing properties of an
        animation effect=] of |group| from |timing input|.

        If that procedure causes an exception to be thrown, propagate the
        exception and abort this procedure.

    1.  <a lt="insert children">Insert</a>
        {{GroupEffect/GroupEffect(children, timing)/children}}
        before <code>null</code>.

    <div class="parameters">

    :    <dfn argument
          for="GroupEffect/GroupEffect(children, timing)"
          lt="children">children</dfn>
    ::   A sequence of animation effects to add as children of this group.

          These children are appended in sequence using the same
          semantics as the {{GroupEffect/append()}} method of the
          {{GroupEffect}} interface.

    :    <dfn argument
          for="GroupEffect/GroupEffect(children, timing)"
          lt="timing">timing</dfn>
    ::   The timing properties or <a>iteration duration</a> of the new
          group effect.

    </div>

</div>

<div class="attributes">

:   <dfn attribute for=GroupEffect>children</dfn>
::  The list of <a>child effects</a> in the group.

:   <dfn attribute for=GroupEffect>firstChild</dfn>
::  The <a>first child</a> of this <a>group effect</a>.

:   <dfn attribute for=GroupEffect>lastChild</dfn>
::  The <a>last child</a> of this <a>group effect</a>.

</div>

<div class="methods">

:   <dfn method for=GroupEffect lt="prepend()">
:   undefined prepend (AnimationEffect... effects)</dfn>
::

    1.  If any of the <a>animation effects</a> in <var>effects</var> is an
        <a>inclusive ancestor</a> of this <a>animation effect</a>,
        <a>throw</a> a <span class=exceptionname>HierarchyRequestError</span>
        exception and abort these steps.
    2.  <a lt="insert children">Insert</a> <var>effects</var> before
        the <a>first child</a>.

:   <dfn method for=GroupEffect lt="append()">
    undefined append (AnimationEffect... effects)</dfn>
::

    1.  If any of the <a>animation effects</a> in <var>effects</var> is an
        <a>inclusive ancestor</a> of this <a>animation effect</a>,
        <a>throw</a> a <span class=exceptionname>HierarchyRequestError</span>
        exception and abort these steps.
    2.  <a lt="insert children">Insert</a> <var>effects</var> before
        <code>null</code>.

:   <dfn method for=GroupEffect lt="clone()">
    GroupEffect clone ()</dfn>
::  Creates a deep copy of this {{GroupEffect}} object using the
    following procedure.

    1.  Let <var>source</var> be this {{GroupEffect}} object,
        the object to be cloned.

    2.  Let <var>cloned timing</var> be a new
        {{EffectTiming}} object whose members are assigned
        the value of the attribute with the same name on
        <code><var>source</var>.getTiming()</code>.

    3.  Let <var>cloned children</var> be an empty sequence of
        {{AnimationEffect}} objects.

    4.  For each <var>child</var> in
        <code><var>source</var>.children</code>, append the result
        of calling <code><var>child</var>.clone()</code>
        to <var>cloned children</var>.

    5.  Return a new {{GroupEffect}} object created by
        calling the {{GroupEffect()}} constructor with parameters
        <code>GroupEffect(<var>cloned children</var>,
        <var>cloned timing</var>)</code>.

</div>

<h4 id="processing-a-timing-argument">Processing a <code>timing</code> argument</h4>

<p>The <var>timing</var> parameter passed to the
{{GroupEffect()}} or {{SequenceEffect()}}
constructor may be an {{EffectTiming}} object, a double
representing the duration of the <a>animation effect</a> in milliseconds, or
undefined.</p>

<p>The following procedure to <dfn>process a timing argument</dfn>,
<var>timing</var>, normalizes the above inputs into
a {{EffectTiming}} object.</p>

    :   If <var>timing</var> is an {{EffectTiming}} object,
    ::  Return <var>timing</var>.

    :   If <var>timing</var> is a <code>double</code>,
    ::  Return a new {{EffectTiming}} object with all members set
        to their default values and {{EffectTiming/duration}} set
        to <var>timing</var>.

    :   Otherwise (<var>timing</var> is undefined),
    ::  Return a new {{EffectTiming}} object with all members set
        to their default values.

<h4 id="definitions-for-manipulating-hierarchies">Definitions for manipulating hierarchies</h4>

The <dfn lt="next sibling not included">next sibling of
<var>effect</var> not included</dfn> in a set of <a>animation
effects</a>, <var>effects</var> is determined using the following
steps:

1.  Let <var>context effect</var> be <var>effect</var>.
2.  While the <a>next sibling</a> of <var>context effect</var> is not
    <code>null</code> perform the following steps:

    1.  Let <var>context effect</var> be the <a>next sibling</a> of
        <var>context effect</var>.
    2.  If <var>context effect</var> is not in <var>effects</var> return
        <var>context effect</var> and abort these steps.

3.  Return <code>null</code>.

To <dfn lt="remove an animation effect">remove</dfn> a
<var>effect</var> from its <a>parent group</a> or
<a>animation</a>, perform the steps corresponding to the first
matching condition from below, if any:

<dl class="switch">

    :   If <var>effect</var> has a <a>parent group</a>,
    ::  Remove <var>effect</var> from the <a>parent group</a>'s
        list of <a>child effects</a>.

    :   If <var>effect</var> is <a>directly associated with
        an animation</a>,
    ::  Disassociate <var>effect</var> from the <a>animation</a>.

</dl>


To <dfn lt="insert children">insert</dfn> a series of zero or
more <a>animation effects</a>, <var>effects</var>, to
<var>parent</var>'s list of <a>child effects</a> before
<var>reference child</var> perform the following steps for each
<var>effect</var> in <var>effects</var>:

1.  <a lt="remove an animation effect">Remove</a> <var>effect</var>
    from its parent.
2.  Insert <var>effect</var> to <var>parent</var>'s list of <a>child
    effects</a> before <var>reference child</var>

<h3 id="the-animationnodelist-interface">The <code>AnimationNodeList</code> interface</h3>

A list of <a>animation effects</a> may be represented by
an {{AnimationNodeList}}.

The <code>AnimationNodeList</code> interface supports indexed
properties with indices in the range 0 &le; <var>index</var> &lt;
<code>length</code>.

<p class="note">
The only reason this interface exists is to provide a familiar
experience for authors familiar with DOM interfaces where child nodes
are accessed via a <code>children</code> member.
</p>

<pre class='idl'>
[Exposed=Window]
interface AnimationNodeList {
    readonly attribute unsigned long length;
    getter AnimationEffect? item (unsigned long index);
};
</pre>

<div class="attributes">

:   <dfn attribute for=AnimationNodeList>length</dfn>
::  The number of <a>animation effects</a> in the list.

</div>

<div class="methods">

:   <dfn method for=AnimationNodeList lt="item(index)">
    getter AnimationEffect? item(unsigned long index)</dfn>
::  Returns the <a>animation effect</a> at <code>index</code>.
    If <code>index</code> is greater than or equal to
    <code>length</code> returns <code>null</code>.

</div>

<h3 id="the-sequenceeffect-interface">The <code>SequenceEffect</code> interfaces</h3>

<a>Sequence effects</a> are represented by the {{SequenceEffect}} interface.

<pre class='idl'>
[Exposed=Window]
interface SequenceEffect : GroupEffect {
  constructor(sequence&lt;AnimationEffect>? children,
              optional (unrestricted double or EffectTiming) timing = {});

  SequenceEffect clone ();
};
</pre>

<div class="constructors">

:   <dfn constructor for=SequenceEffect lt="SequenceEffect(children, timing)">constructor (sequence&lt;AnimationEffect&gt;? children,
    optional (unrestricted double or EffectTiming) timing)</dfn>
::  The meaning and handling of each of the parameters in this
    constructor is identical to the {{GroupEffect()}} constructor.

</div>

<div class="methods">

:   <dfn method for=SequenceEffect lt="clone()">
    SequenceEffect clone ()</dfn>
::  Creates a deep copy of this {{SequenceEffect}} object using
    the same procedure as defined for the {{GroupEffect/clone()}}
    method of the {{GroupEffect}} interface except that a new
    {{SequenceEffect}} object is created.

</div>

<h3 id="the-keyframeeffect-interface">The <code>KeyframeEffect</code> interfaces</h3>

The {{KeyframeEffect!!interface}} interface is modified to add the following:

<pre class='idl'>
partial interface KeyframeEffect {
    attribute IterationCompositeOperation    iterationComposite;
};
</pre>

<div class="constructors">

:   <dfn constructor for=KeyframeEffect
     lt="KeyframeEffect(target, keyframes, options)">
    KeyframeEffect (target, keyframes, options)</dfn>
::  Amend step 5 of the procedure to create a new {{KeyframeEffect!!interface}} object as
    follows:

    5.  If <var>options</var> is a {{KeyframeEffectOptions}} object,
        assign the {{KeyframeEffect/iterationComposite}}, and
        {{KeyframeEffect/composite}}, properties of <var>effect</var> to the
        corresponding value from <var>options</var>.

        When assigning these properties, the error-handling defined for the
        corresponding setters on the {{KeyframeEffect!!interface}} interface is applied.
        If any of those setters require an exception to be thrown
        for the values specified by <var>options</var>, this procedure must
        <a>throw</a> the same exception and abort all further steps.

:   <dfn constructor for=KeyframeEffect lt="KeyframeEffect(source)">KeyframeEffect (source)</dfn>
::  Amend the procedure to create a new {{KeyframeEffect!!interface}} object with the
    same properties as {{KeyframeEffect/KeyframeEffect(source)/source}} to include setting the
    <a>iteration composite operation</a> from <var>source</var> on <var>effect</var>.

</div>

<div class="attributes">

:   <dfn attribute for=KeyframeEffect>iterationComposite</dfn>
::  The <a>iteration composite operation</a> property of this
    <a>keyframe effect</a> as specified by one of the
    <a>IterationCompositeOperation</a> enumeration values.

    On setting, sets the <a>iteration composite operation</a> property of this
    <a>animation effect</a> to the provided value.

</div>

<h4 id="creating-a-new-keyframeeffect-object">Creating a new <code>KeyframeEffect</code> object</h4>

<div class='informative-bg'><em>This section is non-normative</em>

Replace:

>     If the duration is not specified, a value of zero is used.

with:

>     If the duration is not specified, the [=intrinsic iteration duration=] is
>     used.

Add:

> This is particularly useful in combination with other <a>animation
> effects</a>.  For example, fading an element before switching 'visibility' to
> &lsquo;hidden&rsquo; can be achieved as follows,

<div class='example'><pre class='lang-javascript'>
new SequenceEffect(
  [
    new KeyframeEffect(elem, { opacity: 0 }, 1000),
    new KeyframeEffect(elem, { visibility: 'hidden' }, { fill: 'forwards' })
  ]
);</pre></div>

</div>

<h4 id="keyframe-offset-type">Modification to the <code>*Keyframe</code> dictionaries</h4>

The <code>double?</code> type of the <code>offset</code> key
in <code>ComputedKeyframe</code>,
<code>BaseComputedKeyframe</code>,
<code>BaseKeyframe</code>,
<code>BasePropertyIndexedKeyframe</code>,
etc.
is replaced by <code>(CSSNumberish? or TimelineRangeOffset or DOMString)</code>,
where <code>double</code> continues to be interpreted as a percentage,
a {{DOMString}} is parsed into a {{TimelineRangeOffset}}
according to the syntax of <<keyframe-selector>>,
and the resulting {{TimelineRangeOffset}} or {{CSSNumericValue}}
is interpreted as defined for ''@keyframes''.

If the {{DOMString}} fails to parse as a valid <<keyframe-selector>>,
then the keyframe is invalid,
and will throw a <span class=exceptionname>TypeError</span>
when [[web-animations-1#processing-a-keyframes-argument|processed]]
(same as if the <code>double</code> value was outside the [0,1] range).

<h4 id="the-keyframeeffectoptions-dictionary">The KeyframeEffectOptions dictionary</h4>

The {{KeyframeEffectOptions}} dictionary interface is modified to add the
following member:

<pre class='idl'>
partial dictionary KeyframeEffectOptions {
    IterationCompositeOperation iterationComposite = "replace";
};
</pre>

<div class="members">

:   <dfn dict-member for=KeyframeEffectOptions>iterationComposite</dfn>
::  The <a>iteration composite operation</a> used to define the way
    animation values build from iteration to iteration.

<h3 id="the-iterationcompositeoperation-enumeration">The IterationCompositeOperation enumeration</h3>

The possible values of an <a>animation effect</a>'s
<a>iteration composite operation</a> are represented by the
<dfn>IterationCompositeOperation</dfn> enumeration.

<pre class='idl'>
enum IterationCompositeOperation { "replace", "accumulate" };
</pre>

:   <code>replace</code>
::  Corresponds to the <a
    lt="iteration composite operation replace">replace</a>
    <a>iteration composite operation</a> value such that the
    <a>effect value</a> produced is independent of the
    [=current iteration index|current iteration=].
:   <code>accumulate</code>
::  Corresponds to the <a
    lt="iteration composite operation accumulate">accumulate</a>
    iteration composite operation value such that
    subsequent iterations of an <a>animation effect</a> build
    on the final value of the previous iteration.

<h3 id="the-effectcallback-callback-function">The <code>EffectCallback</code> callback function</h3>

<ins cite="#amend-CE">

<a>Custom effects</a> can be defined in script by providing an
{{EffectCallback}} callback function.

<pre class='idl'>
callback EffectCallback = undefined (double? progress,
                                (Element or CSSPseudoElement) currentTarget,
                                Animation animation);
</pre>

An {{EffectCallback}} is called each time an {{KeyframeEffect!!interface}} object
with which it is associated is updated.

<div class="parameters">

:   double? progress
::  The <a>iteration progress</a> value for which to produce an effect.
    When this is <code>null</code>, the function SHOULD
    remove the effect.

:   (Element or CSSPseudoElement) currentTarget
::  The [=effect target/target element=] on which this callback is expected
    to operate.

    <div class="note">

    Note that <var>currentTarget</var> may differ from
    <code><var>animation</var>.target</code>.

    If the [=effect target/target element=] of <var>animation</var>
    is changed between updates, this method will be
    called once with a null <var>progress</var> and the
    previous [=effect target/target element=] as the
    <var>currentTarget</var>, then again with the current
    <var>progress</var> and the updated <a>target
    element</a> as the <var>currentTarget</var>.
    This allows the animation effect to be removed from the old
    [=effect target/target element=].

    </div>

:   Animation animation
::  The {{Animation}} object that is being updated.

</div>

</ins>

<h3 id="the-animatable-interface">The <code>Animatable</code> interface</h3>

<div dfn-type=method class=methods
       dfn-for="Animatable">

:   <dfn lt="getAnimations(options)">sequence&lt;Animation&gt; getAnimations(|options|)</dfn>
::  Add:

    > If this object is the [=effect target/target element=] of two or more
    > <a>animation effects</a> which are associated with the
    > same <a>animation</a>, the corresponding {{Animation}}
    > object will still only appear in the returned list once.

</div>

The <code>Animatable</code> interface mixin {#the-animatable-interface-mixin}
----------------------------------------

The {{Animatable}} mixin interface
is modified to add the following members:

<div dfn-type=method class=methods
       dfn-for="Animatable">

  : <dfn lt="animate(keyframes, options)">Animation animate(<var ignore=''>keyframes</var>, <var>options</var>)</dfn>
  ::  Amend step 6 of the procedure as follows:

    6. If |options| is a {{KeyframeAnimationOptions}} object,
        let |trigger| be the <code>trigger</code> member of |options|
        or, if <code>trigger</code> member of |options| is missing,
        a newly created {{AnimationTrigger}} with an empty
        {{AnimationTriggerOptions}} dictionary as its options.

    7. Run the procedure to [=setting a trigger of an animation=] for |animation|
        with the |auto-rewind| flag set to true.

</div>

<wpt>
    interfaces/Animatable/animate-no-browsing-context.html
    interfaces/Animatable/animate.html
    interfaces/Animatable/getAnimations-iframe.html
    interfaces/Animatable/getAnimations.html
</wpt>

The {{KeyframeAnimationOptions}} dictionary interface
is modified to add the following members:

<pre class='idl'>
dictionary TimelineRangeOffset {
  CSSOMString? rangeName;
  CSSNumericValue offset;
};

partial dictionary KeyframeAnimationOptions {
    (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart = "normal";
    (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd = "normal";
    AnimationTrigger? trigger;
};
</pre>

<div class="members">

:   <dfn dict-member for=KeyframeAnimationOptions>rangeStart</dfn>
::  If present and not "normal",
    specifies the start of the <a>animation</a>’s [=animation attachment range=].
    A {{DOMString}} value is parsed as an 'animation-range-end' value
    to yield a {{TimelineRangeOffset}} {{TimelineRangeOffset/rangeName}}
    and {{TimelineRangeOffset/offset}}
    or alternatively a {{CSSKeywordValue}} with the {{CSSKeywordValue/value}} of "normal";
    a {{CSSNumericValue}} is interpreted as a {{TimelineRangeOffset}}
    with that {{TimelineRangeOffset/offset}} and a null {{TimelineRangeOffset/rangeName}}.

:   <dfn dict-member for=KeyframeAnimationOptions>rangeEnd</dfn>
::  If present and not "normal",
    specifies the end of the <a>animation</a>’s [=animation attachment range=].
    A {{DOMString}} value is parsed as an 'animation-range-end' value
    to yield a {{TimelineRangeOffset}} {{TimelineRangeOffset/rangeName}}
    and {{TimelineRangeOffset/offset}};
    a {{CSSNumericValue}} is interpreted as a {{TimelineRangeOffset}}
    with that {{TimelineRangeOffset/offset}} and a null {{TimelineRangeOffset/rangeName}}.

:   <dfn dict-member for=KeyframeAnimationOptions>trigger</dfn>
::  If present, specifies the <a>animation</a>’s associated [=animation trigger=]
    by running the procedure of [=setting a trigger of an animation=].

</div>

Passing a {{CSSKeywordValue}} with a {{CSSKeywordValue/value}} other than "normal"
as the {{KeyframeAnimationOptions/rangeStart}} or {{KeyframeAnimationOptions/rangeEnd}} value
to any API defined to accept {{KeyframeAnimationOptions}}
[=throws=] a <span class="exceptionname">TypeError</span>.

Note: The {{TimelineRangeOffset/rangeName}} and {{TimelineRangeOffset/offset}}
values of {{KeyframeAnimationOptions/rangeStart}} and {{KeyframeAnimationOptions/rangeEnd}}
are interpreted as defined for 'animation-range-start'/'animation-range-end',
and affect the animation accordingly.

See [[web-animations-1#the-animatable-interface-mixin]] for further description.

<h3 id="the-animationplaybackevent-interface">The <code>AnimationPlaybackEvent</code> interface </h3>

Replace double with CSSNumberish as the type for currentTime and timelineTime.

<pre class='idl'>
[Exposed=Window]
interface AnimationPlaybackEvent : Event {
    constructor(DOMString type, optional AnimationPlaybackEventInit
    eventInitDict = {});
    readonly attribute CSSNumberish? currentTime;
    readonly attribute CSSNumberish? timelineTime;
};
dictionary AnimationPlaybackEventInit : EventInit {
    CSSNumberish? currentTime = null;
    CSSNumberish? timelineTime = null;
};
</pre>

<div class="attributes">

Update the type for the AnimationPlaybackEvent attributes.

:   <dfn attribute for=AnimationPlaybackEvent>currentTime</dfn>
::  The [=animation/current time=] of the [=animation=] that generated the event
    at the moment the event as queued.
    This will be <code class=esvalue>null</code> if the [=animation=] was
    [=play state/idle=] at the time the event was generated.
:   <dfn attribute for=AnimationPlaybackEvent>timelineTime</dfn>
::  The [=time value=] of the [=timeline=] with which the [=animation=] that
    generated the event is associated at the moment the event was queued.
    This will be <code class=esvalue>null</code> if the [=animation=] was not
    associated with an <a lt="inactive timeline">active timeline</a> at the
    time the event was queued.

</div>


<div class="members">

Update the type for the AnimationPlaybackEventInit members.

:   <dfn dict-member for=AnimationPlaybackEventInit>currentTime</dfn>
::  See the description of the {{AnimationPlaybackEvent/currentTime}} attribute.
:   <dfn dict-member for=AnimationPlaybackEventInit>timelineTime</dfn>
::  See the description of the {{AnimationPlaybackEvent/timelineTime}}
    attribute.

</div>

<h3 id="the-animationtrigger-interface">The <code>AnimationTrigger</code> interface</h3>

<pre class='idl'>
[Exposed=Window]
interface AnimationTrigger {
  constructor(optional AnimationTriggerOptions options = {});
  attribute AnimationTimeline timeline;
  attribute AnimationTriggerBehavior behavior;
  attribute any rangeStart;
  attribute any rangeEnd;
  attribute any exitRangeStart;
  attribute any exitRangeEnd;
};
</pre>

<wpt>
    interfaces/TimelineTrigger/constructor.html
</wpt>

<div dfn-type=constructor class=constructors dfn-for="AnimationTrigger">
  : <dfn lt="AnimationTrigger(options)">AnimationTrigger(|options|)</dfn>
  ::
      Creates a new {{AnimationTrigger}} object using the following procedure:

      1. Create a new {{AnimationTrigger}} object, |trigger|.

      1. Set |trigger|.[=did trigger=] to false.

      1. Set |trigger|.[=animation trigger state|state=] to [=animation trigger state/idle=].

      1. Set |trigger|.{{AnimationTrigger/behavior}} to |options|.{{AnimationTriggerOptions/behavior}}.

      1. Set [=default range=] of |trigger| using |options|.{{AnimationTriggerOptions/rangeStart}} and |options|.{{AnimationTriggerOptions/rangeEnd}},
          following the same rules as the KeyframeAnimationOption
          <a href="#dom-keyframeanimationoptions-rangestart">rangeStart</a> and
          <a href="#dom-keyframeanimationoptions-rangeend">rangeEnd</a> respectively.

      1. Set [=exit range=] of |trigger| using |options|.{{AnimationTriggerOptions/exitRangeStart}} and |options|.{{AnimationTriggerOptions/exitRangeEnd}},
          following the same rules above for [=default range=], unless they are set to
          "auto", in which case they are set to their corresponding side
          of the [=default range=].

      1. Run the procedure for [=setting timeline of animation trigger=]
          on |trigger| passing |options|.{{AnimationTriggerOptions/timeline}} as the <var ignore>new timeline</var>.

      <div dfn-type=argument class=parameters
           dfn-for="AnimationTrigger/AnimationTrigger(options)">

          : <dfn>options</dfn>
          ::  Configuration parameters for the newly-created trigger.

      </div>

</div>

<div dfn-type=attribute class=attributes
       dfn-for="AnimationTrigger">

  : <dfn>timeline</dfn>
  ::
      Returns the [=timeline=] for this trigger
      or `null` if this timeline is [=inactive timeline|inactive=].

  : <dfn>behavior</dfn>
  ::
      Returns the [=animation trigger behavior|behavior=] of this trigger.

  : <dfn>rangeStart</dfn>
  ::
      Returns the [=default range=]’s start of this trigger.

  : <dfn>rangeEnd</dfn>
  ::
      Returns the [=default range=]’s end of this trigger.

  : <dfn>exitRangeStart</dfn>
  ::
      Returns the [=exit range=]’s start of this trigger.

  : <dfn>exitRangeEnd</dfn>
  ::
      Returns the [=exit range=]’s end of this trigger.

</div>

<h3 id="the-animationtriggeroptions-dictionary">The <code>AnimationTriggerOptions</code> dictionary</h3>

<pre class='idl'>
dictionary AnimationTriggerOptions {
  AnimationTimeline? timeline;
  AnimationTriggerBehavior? behavior = "once";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart = "normal";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd = "normal";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeStart = "auto";
  (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeEnd = "auto";
};
</pre>

<div dfn-type=dict-member class=members
       dfn-for="AnimationTriggerOptions">

  : <dfn>timeline</dfn>
  ::
      The timeline to which the trigger is associated.
      If not specified, the trigger is associated with [=default document timeline=].

  : <dfn>behavior</dfn>
  ::
      The behavior of the trigger.
      If not specified, the trigger's behavior is ''animation-trigger-type/once''.

  : <dfn>rangeStart</dfn>
  ::
      The start of the trigger’s [=default range=].
      If not specified, the [=default range=]’s start is set to "normal".

  : <dfn>rangeEnd</dfn>
  ::
      The end of the trigger’s [=default range=].
      If not specified, the [=default range=]’s end is set to "normal".

  : <dfn>exitRangeStart</dfn>
  ::
      The start of the trigger’s [=exit range=].
      If not specified, the [=exit range=]’s start is set to "auto".

  : <dfn>exitRangeEnd</dfn>
  ::
      The end of the trigger’s [=exit range=].
      If not specified, the [=exit range=]’s end is set to "auto".

</div>

<h3 id="the-animationtriggerbehavior-enumeration">The <code>AnimationTriggerBehavior</code> enumeration</h3>

<pre class='idl'>
enum AnimationTriggerBehavior { "once", "repeat", "alternate", "state" };
</pre>

<dl dfn-type=enum-value
      dfn-for="AnimationTriggerBehavior">

  : <dfn>once</dfn>
  ::
      ''animation-trigger-behavior/once''.

  : <dfn>repeat</dfn>
  ::
      ''animation-trigger-behavior/repeat''.

  : <dfn>alternate</dfn>
  ::
      ''animation-trigger-behavior/alternate''.

  : <dfn>state</dfn>
  ::
      ''animation-trigger-behavior/state''.

</dl>

<h3 id="model-liveness">Model liveness</h3>

<div class="informative-bg"><em>This section is non-normative</em>

Regarding the section on, &ldquo; Changes made to the Web Animations model take
effect immediately&rdquo;, add:

>     The same concept applies to more complex modifications of the
>     Web Animations model such as adding and removing children from
>     an {{GroupEffect}}.

Add:

> :   Changes made to the model using the programming interface do
>     <em>not</em> cause any {{EffectCallback}} functions to be
>     called
>
> ::  For example, in the following code, the callback function will
>     not be called until <em>after</em> the script block has
>     completed during regular updating.
>
>     <div class="example">
>     <pre class="lang-javascript">
> var timesCalled = 0;
> elem.animate(function() {
>   timesCalled++;
> }, 10000);
> alert(timesCalled); // Displays &lsquo;0&rsquo;</pre>
>     </div>
>
>     Note: Need to spec this properly somewhere.

</div>

<h2 id="changes">Changes</h2>

<h3 id="changes-since-level-1">Changes since level 1</h3>

This specification introduces the following changes compared to the
previous level of this specification:

*   <a>group effects</a> and <a>sequence effects</a>,
*   an <a>animation effect</a>-specific
    [=animation effect/playback rate=],
*   Support for non-monotonic (scroll) timelines.

<div class="amendment" id=amend-CE>
This module also includes some exploratory proposals for [[#custom-effects]],
however there are some concerns about the design of this feature
and it may be replaced in a future revision.
See <a href="https://github.com/w3c/csswg-drafts/issues/6861">discussion</a>.
</div>

<h3 id="changes-20230221">
Changes since the <a href="https://www.w3.org/TR/2023/WD-web-animations-2-20230221/">First Public Working Draft</a> of 21 February 2023
</h3>

<ul>
    <!-- to  26 September 2025 -->
    <li>Renamed animation-trigger-type to animation-trigger-behavior</li>
    <li>Corrected mention of start time to current time</li>
    <li>Corrected syntax of rangeStart, rangeEnd, exitRangeStart and exitRangeEnd attributes on AnimationTrigger interface (<a href="https://github.com/w3c/csswg-drafts/issues/11624">Issue 11624</a>)</li>
    <li>Specified the animation-trigger property, added the AnimationTrigger interface <a href="https://github.com/w3c/csswg-drafts/pull/10128"></a></li>
    <li>Changed progress to overallProgress (<a href="https://github.com/w3c/csswg-drafts/issues/8799#issuecomment-2461097047">Issue 8799</a>)</li>
    <li>Specified Animation progress API (<a href="https://github.com/w3c/csswg-drafts/pull/9937">Issue 9937</a>)</li>
    <li>Clarified that start time of the animation is auto-aligned with the animation range boundary when it has not been explicitly set (<a href="https://github.com/w3c/csswg-drafts/pull/9181">Issue 9181</a>)</li>
    <li>Fixed IDL definition of KeyframeAnimationOptions</li>
    <li>Expanded offset syntax for keyframes (<a href="https://github.com/w3c/csswg-drafts/issues/7637">Issue 7637</a>)</li>
    <li>Added rangeStart/rangeEnd to Animatable interface (<a href="https://github.com/w3c/csswg-drafts/issues/7589">Issue 7589</a>)</li>
    <li>Tidied up effect stack definitions (<a href="https://github.com/w3c/csswg-drafts/issues/8497">Issue 8497</a>)</li>
</ul>

Privacy Considerations {#priv}
===============================================

No privacy considerations have been reported on this module.

Security Considerations {#sec}
===============================================

No security considerations have been reported on this module.


<wpt hidden title="not explicitly marked as animtions-2, please check">
animation-model/animation-types/accumulation-per-property-001.html
animation-model/animation-types/accumulation-per-property-002.html
animation-model/animation-types/addition-per-property-001.html
animation-model/animation-types/addition-per-property-002.html
animation-model/animation-types/discrete.html
animation-model/animation-types/interpolation-per-property-001.html
animation-model/animation-types/interpolation-per-property-002.html
animation-model/animation-types/scrollbar-interpolation.html
animation-model/animation-types/visibility.html
animation-model/combining-effects/applying-interpolated-transform.html
animation-model/combining-effects/applying-the-composited-result.html
animation-model/combining-effects/effect-composition.html
animation-model/keyframe-effects/background-shorthand.html
animation-model/keyframe-effects/computed-keyframes-shorthands.html
animation-model/keyframe-effects/effect-in-removed-iframe-crash.html
animation-model/keyframe-effects/effect-on-marquee-parent-crash.html
animation-model/keyframe-effects/effect-value-context-filling.html
animation-model/keyframe-effects/effect-value-context.html
animation-model/keyframe-effects/effect-value-interval-distance.html
animation-model/keyframe-effects/effect-value-iteration-composite-operation.html
animation-model/keyframe-effects/effect-value-opacity-replaced-effect-in-shadow-root.html
animation-model/keyframe-effects/effect-value-opacity-replaced-effect.html
animation-model/keyframe-effects/effect-value-overlapping-keyframes.html
animation-model/keyframe-effects/effect-value-replaced-animations.html
animation-model/keyframe-effects/effect-value-transformed-distance.html
animation-model/keyframe-effects/keyframe-exceptions.html
animation-model/keyframe-effects/transform-and-opacity-on-inline-001.html
animation-model/side-effects-of-animations-current.html
animation-model/side-effects-of-animations-in-effect.html
animation-model/side-effects-of-animations-none.html
crashtests/color-mix-crashtest.html
crashtests/effectively-infinite-timing-parameters.html
crashtests/get-computed-timing-crash.html
crashtests/get-keyframe-fontsize-crash.html
crashtests/get-timing-bad-pseudo-crash.html
crashtests/infinite-active-duration.html
crashtests/non-interpolable-transition.html
crashtests/partially-overlapping-animations-one-not-current-001.html
crashtests/reparent-animating-element-001.html
crashtests/reparent-animating-element-002.html
crashtests/set-timeline-undefined-progress.html
crashtests/sibling-index-offset-crash.html
idlharness.window.js
interfaces/Animatable/animate-no-browsing-context.html
interfaces/Animatable/animate.html
interfaces/Animatable/getAnimations-iframe.html
interfaces/Animatable/getAnimations.html
interfaces/Animation/cancel.html
interfaces/Animation/commitStyles-crash.html
interfaces/Animation/commitStyles-svg-crash.html
interfaces/Animation/commitStyles.html
interfaces/Animation/constructor.html
interfaces/Animation/effect.html
interfaces/Animation/finished.html
interfaces/Animation/id.html
interfaces/Animation/oncancel.html
interfaces/Animation/onfinish.html
interfaces/Animation/onremove.html
interfaces/Animation/overallProgress.html
interfaces/Animation/pause.html
interfaces/Animation/pending.html
interfaces/Animation/persist.html
interfaces/Animation/play.html
interfaces/Animation/ready.html
interfaces/Animation/startTime.html
interfaces/Animation/style-change-events.html
interfaces/AnimationEffect/getComputedTiming.html
interfaces/AnimationEffect/updateTiming.html
interfaces/AnimationPlaybackEvent/constructor.html
interfaces/Document/timeline.html
interfaces/DocumentOrShadowRoot/getAnimations.html
interfaces/DocumentTimeline/constructor.html
interfaces/DocumentTimeline/style-change-events.html
interfaces/KeyframeEffect/composite.html
interfaces/KeyframeEffect/constructor.html
interfaces/KeyframeEffect/copy-constructor.html
interfaces/KeyframeEffect/getKeyframes.html
interfaces/KeyframeEffect/iterationComposite.html
interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html
interfaces/KeyframeEffect/processing-a-keyframes-argument-002.html
interfaces/KeyframeEffect/setKeyframes.html
interfaces/KeyframeEffect/style-change-events.html
interfaces/KeyframeEffect/target.html
interfaces/TimelineTrigger/constructor.html
responsive/assorted-lengths.html
responsive/background-color-animation-plus-opacity.html
responsive/background-position-responsive.html
responsive/backgroundPosition.html
responsive/backgroundSize.html
responsive/baselineShift.html
responsive/borderImageWidth.html
responsive/borderRadius.html
responsive/borderWidth.html
responsive/box-shadow-responsive.html
responsive/boxShadow.html
responsive/clip.html
responsive/columnCount.html
responsive/columnGap.html
responsive/d.html
responsive/font-size-adjust.html
responsive/fontSize.html
responsive/fontWeight.html
responsive/lineHeight.html
responsive/minHeight.html
responsive/neutral-keyframe.html
responsive/offset-path.html
responsive/offsetDistance.html
responsive/offsetRotate.html
responsive/opacity.html
responsive/perspective.html
responsive/rotate.html
responsive/rowGap.html
responsive/shapeMargin.html
responsive/shapeOutside.html
responsive/strokeDasharray.html
responsive/text-size-adjust.html
responsive/textIndent.html
responsive/to-color-change.html
responsive/to-inherited-change.html
responsive/to-style-change.html
responsive/toggle-animated-iframe-visibility.html
responsive/transform.html
responsive/translate.html
responsive/verticalAlign.html
responsive/width.html
timing-model/animation-effects/active-time.html
timing-model/animation-effects/current-iteration.html
timing-model/animation-effects/local-time.html
timing-model/animation-effects/phases-and-states.html
timing-model/animation-effects/simple-iteration-progress.html
timing-model/animations/canceling-an-animation.html
timing-model/animations/document-timeline-animation.html
timing-model/animations/finishing-an-animation.html
timing-model/animations/infinite-duration-animation.html
timing-model/animations/invalidating-animation-before-start-time-synced.html
timing-model/animations/pausing-an-animation.html
timing-model/animations/play-states.html
timing-model/animations/playing-an-animation.html
timing-model/animations/reverse-running-animation.html
timing-model/animations/reversing-an-animation.html
timing-model/animations/seamlessly-updating-the-playback-rate-of-an-animation.html
timing-model/animations/setting-the-current-time-of-an-animation.html
timing-model/animations/setting-the-playback-rate-of-an-animation.html
timing-model/animations/setting-the-start-time-of-an-animation.html
timing-model/animations/setting-the-target-effect-of-an-animation.html
timing-model/animations/setting-the-timeline-of-an-animation.html
timing-model/animations/start-time-compat.html
timing-model/animations/sync-start-times.html
timing-model/animations/the-current-time-of-an-animation.html
timing-model/animations/update-playback-rate-fast.html
timing-model/animations/update-playback-rate-zero.html
timing-model/animations/updating-the-finished-state.html
timing-model/time-transformations/transformed-progress.html
timing-model/timelines/document-timelines.html
timing-model/timelines/sibling-iframe-timeline.html
timing-model/timelines/timelines.html
timing-model/timelines/update-and-send-events-replacement.html
timing-model/timelines/update-and-send-events.html
</wpt>
